大概思路如下:
我们随便点亮一个点集S,假设此时T集合中的点是亮的,就代表着T集合中的每个点到S集合至少有一条边,我们通过整体二分可以找出这条边。可以证明这样找到一条边的期望次数是log的,因此总次数为O(mlogn),加些随机化就过了。
#include<cstdio> #define maxn 1000000 #define mid (l+r>>1) void modify(int x); int query(int x); void report(int x, int y); int check(int x); int s[maxn],a[maxn],z1[maxn],z2[maxn],ans[maxn],v[maxn]; void solve(int l,int r,int L,int R){ if(l==r){ for(int i=L;i<=R;i++) ans[a[i]]=l; return; } for(int i=l;i<=mid;i++) modify(i); int t1=0,t2=0,tt=L-1; for(int i=L;i<=R;i++){ int fl=0; if(query(a[i])^s[a[i]]) s[a[i]]^=1,fl=1; if(fl||(a[i]<=mid)) z1[++t1]=a[i]; else z2[++t2]=a[i]; } for(int i=1;i<=t1;i++) a[++tt]=z1[i];for(int i=1;i<=t2;i++) a[++tt]=z2[i]; solve(l,mid,L,L+t1-1); solve(mid+1,r,L+t1,R); } void explore(int N, int M) { if(N<=500){ for(int i=0;i<N-1;i++){ modify(i); for(int j=i+1;j<N;j++){ if(query(j)^s[j]) report(i,j),s[j]^=1; } } }else if(N%10==8){ for(int i=0,l=1;l<=N;i++,l<<=1){ for(int j=0;j<N;j++){ int x=((j>>i)&1),y=i?((j>>(i-1))&1):0; if(x^y) modify(j); } for(int j=0;j<N;j++){ if(query(j)) v[j]|=1<<i; } } for(int i=0;i<N;i++) if(i<(v[i]^i)) report(i,v[i]^i); }else if(N%10==7){ for(int i=0;i<N;i++)a[i]=i; solve(0,N-1,1,N-1); for(int i=1;i<N;i++)report(ans[i],i); }else if(N%10==6){ for(int i=0,l=1;l<=N;i++,l<<=1){ for(int j=0;j<N;j++){ int x=((j>>i)&1),y=i?((j>>(i-1))&1):0; if(x^y) modify(j); } for(int j=0;j<N;j++){ if(query(j)) v[j]|=1<<i,s[j]=1; else s[j]=0; } } modify(0);int t1=0; for(int i=1;i<N;i++)if(query(i)^s[i]) z1[++t1]=i; for(int i=1;i<=t1;i++){ for(int now=z1[i],lst=0,x;now;x=lst,lst=now,now=v[now]^now^x) report(now,lst); } } }
ps:56分