• P2490[SDOI2011]黑白棋【博弈论,dp】


    正题

    题目链接:https://www.luogu.com.cn/problem/P2490


    题目大意

    一个长度为\(n\)的棋盘上放下\(k\)个棋子。

    第一个要是白色,下一个要是黑色,在下一个是白色以此类推。

    先手操控白,后手操控黑。白色只能往右,黑色只能往左。每次操作的可以移动\(d\)个棋子任意步。

    求先手必胜的初始状态数

    \(1\leq d\leq k\leq n\leq 10^4,1\leq k\leq 100\)\(k\)为偶数


    解题思路

    把两个黑白棋子之间的长度看为石头堆就是一个\(Nim_k\)游戏了。

    \(Nim_k\)游戏的结论就是\(k+1\)进制下各个位置的\(1\)的个数\(\% (k+1)\)等于\(0\)的话先手必败。

    因为先手必胜比较麻烦,考虑减去先手必败的情况

    这个东西和昨天的一道\(ARC\)的题目很像,每个位分开考虑,设\(f_{i,j}\)表示前\(i\)个位都是\(0\)时,用了\(j\)个石头的方案。

    那么转移也十分显然,枚举一个选的倍数\(i\)然后分配到\(\frac{k}{2}\)个石头堆中,方案数就是\(\binom{\frac{k}{2}}{i\times (d+1)}\)

    然后统计答案的时候对于石子和为\(i\)的贡献就是\(\binom{n-\frac{k}{2}-i}{\frac{k}{2}}\)(因为每一堆的个数固定,所以选择起点就好了)

    时间复杂度\(O(nk\log n)\)


    code

    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #define ll long long
    using namespace std;
    const ll N=1e4+10,M=110,P=1e9+7;
    ll n,k,d,ans,C[N][M],f[16][N];
    signed main()
    {
    	scanf("%lld%lld%lld",&n,&k,&d);
    	C[0][0]=1;n-=k;k/=2;d++;
    	for(ll i=1;i<N;i++)
    		for(ll j=0;j<M;j++)
    			C[i][j]=((j?C[i-1][j-1]:0)+C[i-1][j])%P;
    	ll z=0;ans=C[n+2*k][k*2];f[0][0]=1;
    	for(ll p=1;p<=n;p<<=1){
    		z++;
    		for(ll j=0;j<=n;j++)
    			for(ll i=0;j+i*p*d<=n&&i*d<=k;i++)
    				(f[z][j+i*p*d]+=f[z-1][j]*C[k][i*d]%P)%=P;
    	}
    	for(ll i=0;i<=n;i++)
    		(ans+=P-f[z][i]*C[n+k-i][k]%P)%=P;
    	printf("%lld\n",ans);
    	return 0;
    }
    
  • 相关阅读:
    [转载]SETSOCKOPT IOCTLSOCKET 设置非阻塞SOCKET函数
    [转载]FTP协议详解
    复制控制
    高手的C++学习忠告,虚心学习下~~[转载]
    [转载]Linux中硬链接和软链接的区别和联系
    以太网最小帧长度为什么要64个字节
    [转载]HTTP协议详解
    Netstat命令详解

    将1:N关系的界面上"添加现有****"按钮隐藏掉
  • 原文地址:https://www.cnblogs.com/QuantAsk/p/14593710.html
Copyright © 2020-2023  润新知