CF1442F
以下内容搬运自cf官方题解
真是毒瘤。
先考虑对于一张空图怎么做。
如果最后是一张有向无环图,则每一个点有唯一的SG函数值。通过嬴或者是输的询问只能确定目标节点的SG函数值,对于SG函数值相同的点便无能为力。
于是需要 $O(n^2)$ 条边使得每一个点的SG值均不同,而且每次询问只能排除一个点,单个阶段需要 $O(n)$ 次询问,这样无论如何都是无法通过的。
于是需要利用环的性质。
但是有环时不容易判断游戏的结果。
考虑到有一个点有连向自己的边并且该点上有至少一枚棋子,则先手不会失败(没有必胜策略时,操作方可以沿自环移动已保证不败)。
考虑将点集划分为 $S_1$ 和 $S_2$ ,不存在边 $e:i->j$ 同时使 $i \in S_1$ 和 $j \in S_2$ 成立,且:
$$\underset{i \in S_1}{\text{mex}} \space SG_i=|S_1|+1$$
设 $j \in S_2$ 出边连向点与 $S_1$ 交集为 $T_j$ ,且 $T_j$ 互不相同, $S_2$ 中所有点向自己有边。
然后,牛逼的来了,只需要做 $|S_1|$ 次询问,每次询问一个点,便可确定答案。
假设答案是点 $x$ 。
如果询问点 $i$ 时先手必败,说明 $x \notin S_2$ (原因如上),由 $SG_i \space \text{xor} \space SG_x=0$ 知 $x=i$ 。
如果没有先手必败的情况,则平局点集即为 $T_x$ !证明显然。
只需取 $|S_1|=20$ ,连最少的边使 $T_j$ 互不相同即可。
非空图类似,用拓扑序找 $S_1$ ,然后改变最少的边使 $T_j$ 互不相同(可以删边)即可。
稍加计算得改变边数在 $4000$ 以内,可以通过本题。
代码:
#include<cstdio> #include<algorithm> using namespace std; int D=20; int n,m,q,cnt=-1; int fir[1000],in[1000]; int nxt[200000],to[200000]; int tp[1000],pos[1000]; int have[1<<20]; int K=0,A[10000],B[10000]; bool C[10000]; inline void addedge(int a,int b,bool c) { A[K]=a; B[K]=b; C[K++]=c; return; } inline void add(int a,int b) { to[++cnt]=b; in[b]++; nxt[cnt]=fir[a]; fir[a]=cnt; return; } #include<queue> queue<int> que; void topo(void) { for(int i=0;i<n;i++) if(in[i]==0){ tp[cnt++]=i; que.push(i); } while(que.size()>0){ int i=que.front(); que.pop(); for(int j=fir[i];j!=-1;j=nxt[j]){ in[to[j]]--; if(in[to[j]]==0){ tp[cnt++]=to[j]; que.push(to[j]); } } } return; } void changeS(int p,int S) { for(int p1=D;p1>=0;p1--) for(int p2=(p1==D?D:p1-1);p2>=0;p2--) for(int p3=(p2==D?D:p2-1);p3>=0;p3--){ int S2=(S^(1<<p1)^(1<<p2)^(1<<p3))&((1<<D)-1); if(have[S2]==-1){ have[S2]=p; if(p1<D) addedge(p,tp[n-D+p1],!(S>>p1&1)); if(p2<D) addedge(p,tp[n-D+p2],!(S>>p2&1)); if(p3<D) addedge(p,tp[n-D+p3],!(S>>p3&1)); return; } } return; } void edge(void) { for(int i=0;i<n;i++) pos[tp[i]]=i; for(int i=n-D;i<n;i++) for(int j=i+1;j<n;j++) addedge(tp[i],tp[j],1); for(int i=0;i<n;i++){ if(pos[i]>=n-D) continue; addedge(i,i,1); int S=0; for(int p=fir[i];p!=-1;p=nxt[p]){ int j=to[p]; if(pos[j]>=n-D) S|=(1<<(pos[j]-n+D)); } changeS(i,S); } return; } int main(void) { scanf("%d%d%d",&n,&m,&q); if(D>n) D=n; for(int i=0;i<n;i++) fir[i]=-1; for(int i=0;i<(1<<D);i++) have[i]=-1; while(m--){ int a,b; scanf("%d%d",&a,&b); add(a-1,b-1); } cnt=0; topo(); edge(); printf("%d\n",K); for(int i=0;i<K;i++) if(C[i]==1) printf("+ %d %d\n",A[i]+1,B[i]+1); else printf("- %d %d\n",A[i]+1,B[i]+1); fflush(stdout); while(q--){ char s[20]; int S=0; for(int i=0;i<D;i++){ printf("? 1 %d\n",tp[i+n-D]+1); fflush(stdout); scanf(" %s",s); if(s[0]=='L'){ printf("! %d\n",tp[i+n-D]+1); fflush(stdout); break; } if(s[0]=='W') S|=(1<<i); } if(s[0]!='L'){ printf("! %d\n",have[S]+1); fflush(stdout); } scanf(" %s",s); if(s[0]=='W') return 0; } return 0; }

浙公网安备 33010602011771号