A 一看到题,不是一道解不定方程的裸题吗,调了好久exgcd。 其实一个for就好了啊
B 一直WA ON TEST 7真是烦,一想会不会是编号太大了,又写了一个map版本,无用。
调了好久好久才发现有几次询问没有读完mmp
C 调了一晚上,又看了数篇题解,终于看懂了QAQ
这是一道博弈论,借助图论/DP帮助完成。
博弈中有3种状态:必胜,必败,不一定
而我们需要将这些状态一直转移以求得结果。
如果A无论怎么移动,使B下一步必胜,则A的现在状态为必败。
如果A可以移动任意一步,使B下一步必败,则A现在的状态为必胜。
没被搜到就不一定。
大致思路就是这样,考虑具体实现。
1.如果从每个状态正着搜,显然不行。只能由终止状态倒着推
2.每个状态记录一下入度,且每次若转移需要判重! 经计算,queue空间为4*n
3.思路要清晰,要清楚我们输出的是什么东西(我昨晚一直思路混乱,多求了很多没用的东西)
4.注意细节,我好粗心好粗心啊
#include<cstdio> #include<cstring> #include<iostream> #include<algorithm> #define max(a,b) ((a)>(b)?(a):(b)) #define min(a,b) ((a)<(b)?(a):(b)) #define rep(i,a,b) for (int i=a; i<=b; i++) typedef long long ll; using namespace std; #define N 7005 int n,k[2],s[2][N],dp[2][N]; //1 win 0 loop -1 lose int Degree[2][N]; //此时先手者 struct Node { int p,turn; } q[N<<2],u,v; inline void read(int &x) { x=0; char c=getchar(); int f=1; while (c<'0'||c>'9') {if (c=='-') f=-1; c=getchar();} while (c>='0'&&c<='9') {x=10*x+c-'0'; c=getchar();} x*=f; } inline void bfs() { int f=0,r=2; u.p=1; u.turn=0; q[0]=u; dp[0][1]=-1; u.turn=1; q[1]=u; dp[1][1]=-1; rep(i,1,n) rep(j,0,1) Degree[j][i]=k[j]; //此时先手者 while (f!=r) { u=q[f++]; int now=u.turn; rep(i,1,k[now^1]) { //len!!!!!!!!!!!!!!!!!!!!!!!!!!!! int dot=(u.p-s[now^1][i]+n-1)%n+1; //% !!!!!!!!!! if (dot==1) continue; //can't be realized!!!!!!!! if (dp[now][u.p]==-1) { if (dp[now^1][dot]==1) continue; dp[now^1][dot]=1; v.turn=u.turn^1; v.p=dot; q[r++]=v; } else if (dp[now][u.p]==1) { // if (dp[now^1][dot]==1) continue; if win,can't lose!! if ((--Degree[now^1][dot])==0) { dp[now^1][dot]=-1; v.turn=u.turn^1; v.p=dot; q[r++]=v; } } } } } int main() { // freopen("1.in","r",stdin); read(n); rep(i,0,1) {read(k[i]); rep(j,1,k[i]) read(s[i][j]);} bfs(); rep(i,0,1) { rep(j,2,n) if (dp[i][j]==1) printf("Win "); else if (dp[i][j]==-1) printf("Lose "); else printf("Loop "); puts(""); } return 0; }
D 线段树/虚拟点优化建图
直接建图不可能,这种区间题肯定是裸的线段树
建两棵线段树,每个节点代表一个区间,对于2、3操作,最多连log条边。
第1棵:上到下连边 ;第2棵:下到上连边
操作2:点到tree1的node连边
操作3:tree2的node到点连边
这个转化挺巧妙的。。
自己果然还是菜