LG P5473 [NOI2019] I 君的探险
Description
时隔半年,I 君的商店终于开不下去了,他决定转让商店,做一名探险家去探索未知的广阔世界。
根据古书记载,他在一个大荒漠的腹地找到了未知文明创造的地下宫殿,宫殿由 $N$ 个大型洞穴和 $M$ 条连接这些洞穴的双向通路构成。I 君能借助古书分辨所处的洞穴,但书中并没有记录 $M$ 条通路的连接结构,因此他难以搜寻传说中藏在宫殿里的无尽财宝。
不过现在 I 君发现了一个神秘机关,通过它可以获知宫殿的信息,I 君决定利用这个机关来得到宫殿的连接结构,请你来协助他。
地下宫殿可以抽象成一张 $N$ 个点、$M$ 条边的无向简单图(简单图满足任意两点之间至多存在一条直接相连的边),洞穴从 $0 \sim n - 1$ 编号。目前你并不知道边有哪些。
每个洞穴都拥有一个光源,光源有开启、关闭两种状态,只有当光源处于开启状态时它所在的洞穴才会被照亮。初始时所有的光源都处于关闭状态,而光源的状态只能用I 君发现的神秘机关改变。更具体的,使用神秘机关可以进行如下四种操作:
1. 向机关给定一个编号 $x$,机关将会改变$x$ 号洞穴,以及与$x$ 号洞穴有通路直接相连的洞穴的光源状态。即原来开启的光源将会关闭;原来关闭的光源将会开启。
2. 向机关给定一个编号 $x$,机关将会显示当前$x$ 号洞穴光源的状态。
3. 向机关给定两个编号 $x, y$,表示你确定有一条连接 $x$ 号洞穴与 $y$ 号洞穴的通路,并让机关记录。
4. 向机关给定一个编号 $x$,机关将会判断与 $x$ 号洞穴相连的通路是否都已被记录。
机关在完成上一次操作后才能进行下一次操作。机关不能随意使用,因此每种操作的使用次数都有限制,分别为 $L_m, L_q, M, L_c$。你的任务是,编写一个程序,帮助 I 君决定如何合理利用神秘机关,从而正确地找到这 $M$ 条通路。
Solution
需要写多个部分分
第一个部分分:每次改变某个点的状态,枚举其它点状态是否变化,若变化则有连边
第三个部分分:保证数据为一棵树,整体二分,每次改变$[l,mid]$的状态,如果询问范围中的点满足$\leq mid$或状态改变则连边在分治范围左侧
第四个部分分:保证数据为一条链,二进制分组,每次改变二进制位某一位为1的所有点的状态,如果询问范围中的点只满足该位为1或状态改变中的一个,那么它相连的两个点的该位上的异或和为1,暴力查找0点所连的边,由0点开始向链的两侧扩展更新答案
第六个部分分:类似第三个部分分,这次需要在一个随机的询问序列上进行整体二分,可以连好某个排列里向前连边是奇数的点,重复做几次即可
其余的部分分可以归为第六个部分分
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include "explore.h" #include<algorithm> #include<iostream> #include<cstdlib> #include<vector> #include<cstdio> #include<cmath> using namespace std; void modify(int x); int query(int x); void report(int x,int y); int check(int x); namespace task1{ int sta[505]; void main(int n){ for(int i=0;i<=n-2;i++){ modify(i),sta[i]^=1; for(int j=i+1;j<=n-1;j++)if(sta[j]^query(j))sta[j]^=1,report(i,j); } } } namespace task3{ int q[200005],q1[200005],q2[200005]; void solve(int l,int r,int ql,int qr){ if(ql>qr)return; if(l==r){ for(int i=ql;i<=qr;i++)report(l-1,q[i]-1); return; } int mid=l+r>>1,tot1=0,tot2=0; for(int i=l;i<=mid;i++)modify(i-1); for(int i=ql;i<=qr;i++) if(q[i]<=mid||query(q[i]-1))q1[++tot1]=q[i]; else q2[++tot2]=q[i]; for(int i=l;i<=mid;i++)modify(i-1); for(int i=1;i<=tot1;i++)q[ql+i-1]=q1[i]; for(int i=1;i<=tot2;i++)q[ql+tot1+i-1]=q2[i]; solve(l,mid,ql,ql+tot1-1),solve(mid+1,r,ql+tot1,qr); } void main(int n){ report(0,1); for(int i=3;i<=n;i++)q[i]=i; solve(1,n,3,n); } } namespace task4{ int p[200005]; void dfs(int k,int fa){ if(!(fa^p[k]))return; report(k,fa^p[k]),dfs(fa^p[k],k); } void main(int n){ for(int i=0;(1<<i)<n;i++){ for(int j=0;j<n;j++)if(j>>i&1)modify(j); for(int j=0;j<n;j++)if((j>>i&1)^query(j))p[j]|=1<<i; for(int j=0;j<n;j++)if(j>>i&1)modify(j); } modify(0); for(int i=1;i<n;i++)if(query(i))report(0,i),dfs(i,0); } } namespace task6{ int cnt,tot,id[200005],q[200005],sta[200005],q1[200005],q2[200005]; bool vst[200005]; vector<int>ve[200005]; bool calc(int x){ bool ret=query(id[x]-1); for(int i=0;i<ve[id[x]].size();i++)ret^=sta[ve[id[x]][i]]; return ret; } void solve(int l,int r,int ql,int qr){ if(ql>qr)return; if(l==r){ for(int i=ql;i<=qr;i++)if(q[i]!=l)report(id[l]-1,id[q[i]]-1),ve[id[l]].push_back(id[q[i]]),ve[id[q[i]]].push_back(id[l]),--cnt; return; } int mid=l+r>>1; for(int i=l;i<=mid;i++)if(!vst[id[i]])modify(id[i]-1),sta[id[i]]=1; int tot1=0,tot2=0; for(int i=ql;i<=qr;i++) if(q[i]<=mid||calc(q[i]))q1[++tot1]=q[i]; else q2[++tot2]=q[i]; for(int i=l;i<=mid;i++)if(!vst[id[i]])modify(id[i]-1),sta[id[i]]=0; for(int i=1;i<=tot1;i++)q[ql+i-1]=q1[i]; for(int i=1;i<=tot2;i++)q[ql+tot1+i-1]=q2[i]; solve(l,mid,ql,ql+tot1-1),solve(mid+1,r,ql+tot1,qr); } void main(int n,int m){ cnt=m; for(int i=1;i<=n;i++)id[i]=i; while(cnt){ random_shuffle(id+1,id+n+1),tot=0; for(int i=1;i<=n;i++)if(!vst[id[i]])q[++tot]=i; solve(1,n,1,tot); if(cnt)for(int i=1;i<=n;i++)if(!vst[i]&&check(i-1))vst[i]=true; } } } void explore(int n,int m){ if(n<=500)task1::main(n); else if(n%10==7)task3::main(n); else if(n%10==6)task4::main(n); else task6::main(n,m); }