●CodeChef Sereja and Game
Posted *ZJ
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了●CodeChef Sereja and Game相关的知识,希望对你有一定的参考价值。
题链:
https://www.codechef.com/problems/SEAGM
题解:
概率dp,博弈论
详细题解:http://www.cnblogs.com/candy99/p/6504340.html
本体的先手胜与不胜(第一问)以及胜的概率(第二问)都是通过dp完成的,记忆化搜索实现。
定义了一个非常妙的dp状态:(第一问)
dp[g][c]表示当前选的数的gcd为g,且选了c个数时当前操作的人胜还是不胜。
有了这个状态,配合预处理的cnt[g]数组(表示有cnt[g]个数为g的倍数),
就可以很巧妙的实现dp转移:
0.if(g==1) dp[g][c]=1
1.if(c<cnt[g]&&!dp[g][c+1]) dp[g][c]=1
2.if(gcd(A[i],g)!=g&&!dp[gcd(A[i],g)][c+1]) dp[g][c]=1
第二问与第一问的方法相同,只是换成了随机情况下求概率而已。
(如果看不太懂各种解释,建议直接服用代码。2333)
代码:
#include<bits/stdc++.h> #define MAXN 105 using namespace std; int N,Case; int A[MAXN],cnt[MAXN]; int opt[MAXN][MAXN]; double rad[MAXN][MAXN]; int gcd(int a,int b){ while(b^=a^=b^=a%=b); return a; } int dfs_optimaly(int g,int c){ int &ret=opt[g][c]; if(g==1) ret=1; if(ret!=-1) return ret; ret=0; if(c<cnt[g]&&!dfs_optimaly(g,c+1)) ret=1; for(int i=1;i<=N;i++){ int gg=gcd(g,A[i]); if(gg==g) continue; if(!dfs_optimaly(gg,c+1)) ret=1; } return ret; } double dfs_randomly(int g,int c){ double &ret=rad[g][c]; if(g==1) ret=1; if(ret>-0.5) return ret; ret=0; if(c<cnt[g]) ret+=1.0*(cnt[g]-c)/(N-c)*(1-dfs_randomly(g,c+1)); for(int i=1;i<=N;i++){ int gg=gcd(g,A[i]); if(gg==g) continue; ret+=1.0/(N-c)*(1-dfs_randomly(gg,c+1)); } return ret; } int main(){ for(scanf("%d",&Case);Case;Case--){ scanf("%d",&N); int g=0; for(int i=1;i<=N;i++) scanf("%d",&A[i]),g=gcd(g,A[i]); if(g!=1){printf("%d %.4lf\\n",N&1,1.0*(N&1)); continue;} for(int i=0;i<=100;i++){ cnt[i]=0; for(int j=0;j<=N;j++) opt[i][j]=-1,rad[i][j]=-1; } for(int i=2;i<=100;i++) for(int j=1;j<=N;j++) if(gcd(i,A[j])==i) cnt[i]++; printf("%d ",dfs_optimaly(0,0)); printf("%.4lf\\n",dfs_randomly(0,0)); } return 0; }
以上是关于●CodeChef Sereja and Game的主要内容,如果未能解决你的问题,请参考以下文章
CF 368B Sereja and Suffixes(DP?)
[Codeforces 425A] Sereja and Swaps
Sereja and Array-数组操作或者线段树或树状数组
CF380C Sereja and Brackets 括号序列+线段树