• 【XSY3949】取石子游戏(博弈,线段树)


    题面

    取石子游戏

    题解

    我们把一个有 (n) 个石子, Alice 每次能拿 (a) 个, Bob 每次能拿 (b) 个的堆称为状态 ((n,a,b))。石子数太大的时候不利于分析,尝试简化一下:

    可以证明状态 ((n,a,b)) 的结果同 ((nmod (a+b),a,b)) 状态的结果相同。

    所以我们只需对每一个 (ngets nmod(a+b)),然后只考虑 (n<a+b) 的情况。

    注意两人此时的步数是相同的,所以他们各自的最优策略是最大化“他们自己接下来走的步数-另一个人接下来走的步数”,不妨设这个值对于两人而言是 (s_a,s_b)

    对于每个堆分类讨论:

    • (n<a,b),那么两人都不能取这个堆,直接扔掉。

    • (aleq n<b)(bleq n<a)。不妨是 (aleq n<b)(bleq n<a) 的情况同理),此时这一堆只有 Alice 可以取,那么直接对 (s_a) 贡献 (leftlfloordfrac{n}{a} ight floor)

    • (a,bleq n),注意到 (n<a+b),所以此时只要有一个人在这个堆里取了石子,另一个人就不能取了,换言之前者 “占领” 了这堆石子。那么对于 Alice 来说,如果她 “占领” 了这堆石子,那么她可以多走 (leftlfloordfrac{n}{a} ight floor) 步,Bob 同理。

      可能有人觉得此时 Alice 按 (leftlfloordfrac{n}{a} ight floor) 从大到小 “占领”、Bob 按 (leftlfloordfrac{n}{b} ight floor) 从大到小 “占领” 就可以了,但这并不符合最优策略。

      不妨设满足 (a,bleq n<a+b) 的堆一共有 (m) 堆。对于第 (i) 堆,设如果先手 “占领” 了这堆石子,先手可以多走 (A_i) 步,如果对手 “占领” 了这堆石子,对手可以多走 (B_i) 步。设 (A_i) 的总和为 (SA)(B_i) 的总和为 (SB)

      假设先手取的堆的编号为 (p_1,p_2,cdots,p_k),那么先手和后手的步数差等于:

      [egin{aligned} D_A=&A_{p_1}+cdots+A_{p_k}-(B-B_{p_1}-cdots-B_{p_k})\ =&(A_{p_1}+B_{p_1})+cdots+(A_{p_k}+B_{p_k})-SB end{aligned} ]

      那么后手和先手的步数差也可以同理推出:(D_B=(A_{p_1}+B_{p_1})+cdots+(A_{p_k}+B_{p_k})-SA)

      由于双方都需要优化他们各自的 (D),而 (SA)(SB) 又是一个定值,所以他们都会按 ((A_i+B_i)=leftlfloordfrac{n}{a} ight floor+leftlfloordfrac{n}{b} ight floor) 从大到小轮流 “占领”。

      注意占领也是需要步数的,那么如果 (m) 为奇数,先手会多耗费一步占领。所以为了维护这个东西,不妨把 (A_igets A_i-1)(B_igets B_i-1)

      至于维护这个东西,先把 ((A_i+B_i)) 离线下来排序再用线段树即可。

    所以三种情况我们都处理完了。

    代码如下:

    #include<bits/stdc++.h>
    
    #define N 200010
    #define lc (k<<1)
    #define rc (k<<1|1)
    #define int long long
    
    using namespace std;
    
    inline int read()
    {
    	int x=0,f=1;
    	char ch=getchar();
    	while(ch<'0'||ch>'9')
    	{
    		if(ch=='-') f=-1;
    		ch=getchar();
    	}
    	while(ch>='0'&&ch<='9')
    	{
    		x=(x<<1)+(x<<3)+(ch^'0');
    		ch=getchar();
    	}
    	return x*f;
    }
    
    int n,x[N],a[N],b[N];
    int suma[N],sumb[N];
    int tot,p[N],q[N],rk[N];
    int s[N],pa[N],pb[N];
    int sum[N<<2][2];
    bool size[N<<2];
    
    bool cmp(int a,int b)
    {
    	return p[a]>p[b];
    }
    
    void up(int k)
    {
    	size[k]=size[lc]^size[rc];
    	if(size[lc])
    	{
    		sum[k][1]=sum[lc][1]+sum[rc][0];
    		sum[k][0]=sum[lc][0]+sum[rc][1];
    	}
    	else
    	{
    		sum[k][1]=sum[lc][1]+sum[rc][1];
    		sum[k][0]=sum[lc][0]+sum[rc][0];		
    	}
    }
    
    void update(int k,int l,int r,int x,int y)
    {
    	if(l==r)
    	{
    		size[k]=1;
    		sum[k][1]=y;
    		sum[k][0]=0;
    		return;
    	}
    	int mid=(l+r)>>1;
    	if(x<=mid) update(lc,l,mid,x,y);
    	else update(rc,mid+1,r,x,y);
    	up(k);
    }
    
    signed main()
    {
    //	freopen("ex_C2.in","r",stdin);
    //	freopen("ex_C2_my.out","w",stdout);
    	n=read();
    	for(int i=1;i<=n;i++)
    	{
    		x[i]=read(),a[i]=read(),b[i]=read();
    		x[i]%=(a[i]+b[i]);
    		suma[i]=suma[i-1],sumb[i]=sumb[i-1];
    		s[i]=s[i-1],pa[i]=pa[i-1],pb[i]=pb[i-1];
    		if(x[i]<a[i]&&x[i]<b[i]) continue;
    		if(x[i]<a[i])
    		{
    			sumb[i]+=x[i]/b[i];
    			continue;
    		}
    		if(x[i]<b[i])
    		{
    			suma[i]+=x[i]/a[i];
    			continue;
    		}
    		p[i]=x[i]/a[i]+x[i]/b[i]-2,q[++tot]=i;
    		s[i]+=p[i],pa[i]+=x[i]/a[i]-1,pb[i]+=x[i]/b[i]-1;
    	}
    	sort(q+1,q+tot+1,cmp);
    	for(int i=1;i<=tot;i++) rk[q[i]]=i;
    	bool tag=0;
    	for(int i=1;i<=n;i++)
    	{
    		if(rk[i]) update(1,1,tot,rk[i],p[i]),tag^=1;
    		int fi=sum[1][1];
    //		int se=(s[i]-sum[1])/2;
    		int tmp=suma[i]-sumb[i]+fi-pb[i];
    		bool Af=(tmp>0||(tmp==0&&tag));
    		tmp=sumb[i]-suma[i]+fi-pa[i];
    		bool As=(tmp<0||(tmp==0&&(!tag)));
    		if(Af&&As) puts("Alice");
    		else if((!Af)&&(!As)) puts("Bob");
    		else if(Af) puts("First");
    		else if(As) puts("Second");
    	}
    	return 0;
    }
    /*
    4
    3 2 4
    4 5 2
    1 1 2
    5 3 4
    */
    
  • 相关阅读:
    使用SQL语句创建SQL数据脚本(应对万网主机部分不支持导出备份数据)
    js和jquery页面初始化加载函数的方法及先后顺序
    熔断器原理
    List<T>线性查找和二分查找BinarySearch效率分析
    ASP.NET资源大全-知识分享 【转载】
    C#语法——委托,架构的血液
    SUPERSOCKET 客户端
    VS 中的几种注释方法
    计算机专业术语中英文对照
    2018服务端架构师技术图谱
  • 原文地址:https://www.cnblogs.com/ez-lcw/p/14508509.html
Copyright © 2020-2023  润新知