[NOI2019]I 君的探险
大概思路如下:
我们随便点亮一个点集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分
posted on 2020-07-08 22:55 啊啊啊起个什么名字好 阅读(188) 评论(0) 收藏 举报
浙公网安备 33010602011771号