题目来源于牛客竞赛:https://ac.nowcoder.com/acm/contest/discuss
题目描述:
You are given an undirected graph with N vertices and M edges. The edges are numbered from 1 to M. Denote the set S(x) as: All the vertices we can reach from vertex x by exactly one edge.
You are supposed to deal with Q operations of the following two types: (1,l,r)-- reverse the status of edges numbered between l to r(1≤l≤r≤M). i e.Delete the edge if it is in the graph, otherwise add it to the graph.
(2,u,v) – ask whether S(u) and S(v) are exactly the same (1≤u≤v≤N
).
Note that all the M edges are in the graph at the beginning.
输入描述:
The input contains multiple cases. The first line of the input contains a single positive integer T, the number of cases.
For each case, the first line contains two integers N (1≤N≤100000) and M (1≤M≤200000), the number of vertices and edges in the graph. In the following M lines, the i-th line contains two integers ui,vi(1≤ui,vi≤N), describing the the i-th edge (ui,vi). Each edge appears in the input at most once. The (M+2)-th line contains a integer Q(1≤Q≤200000) , the number of operations. In the following Q lines, each line contains three integers, describing an operation.
The total sum of N over all cases does not exceed 150000. The total sum of \ M Mover all cases does not exceed 800000. The total sum of Q over all cases does not exceed 600000.
输出描述:
For each case, print a string in a line. The length of the string should equal the number of operations of type 2. If the answer is yes, the i-th character of the string should be ‘1’, otherwise it should be ‘0’. Check the samples for more details.
示例1:
输入
1
5 4
1 2
1 3
4 2
4 3
3
2 1 4
1 1 1
2 1 2
输出
10
题解:
显然,我们要用hash等技巧来显式地“表现” S(x)
用到一个技巧:给每一个点一个 long long 范围内的随机数key , 把一个集合S(x)的值定义为其里面的点key的异或值,那么判集合相等就可以用判数值相等来替代。
然后考虑如何维护这些异或值
有一个比较显然的 Q√m log M的做法
我们按点(在原图)的度数分块。对于所有“小点”,每次我们暴力枚举与它相邻的边,看看是否存在,存在的话,用另一侧的点更新它的xor值。至于判断如何存在,其实是一个区间异或、单点查询的问题,我们可以用树状数组做到logM的快速查询。
对于所有“大点”,总数不会超过√M。 我们在每次修改的时候,暴力维护每一次大点的答案即可。这里提供一种可能的方法:对每一个“大点”,维护一个它的边的vector(按标号排序)。 那么每次一个操作对其的贡献的vector里连续一段。我们只要把这连续的一段的xor值给异或掉即可。这样可能需要一个logM的定位。
数据没有特意卡带log的复杂度。
其实优化一下,就能得到一个√M的做法了。
• 对于“小点”,瓶颈是维护“区间异或,单点查询”。注意到修改只有Q个,但询问有 Q√M 个。所以我们可以匀一匀复杂度,改成对边的序列分块。每次修改时,块间打标 记,块内暴力;询问时可以直接O(1)得出结果。
• 对于“大点”,我们还是在每次修改时暴力维护。考虑一开始对边序列分块,那么一个 块最多只会对2*√M个点产生贡献。我们记录一下每一个块对它能影响的每一个大点的 xor值的贡献,以及如果翻转了这个块后的贡献。每次修改时,在块间打翻转标记,两 侧块暴力重构对大点的贡献。每次询问一个大点时,我们枚举所有的块,把所有的块对 它的贡献都异或起来即可。
• 综上,总复杂度是O(Q√M),内存O(M),支持在线。
代码:
#include
#define N 100005
#define M 200005
#define U 1005
using namespace std;
typedef long long LL;
int Go[M<<1],Next[M<<1],Value[M<<1],End[N],cnt;
int be[M],tag[M],u[M],v[M],id[M],val[M];
LL F[U][U],G[U][U],key[N];
int st[U],ed[U],d[N],Q[N],n,m,S,tot;
void add(int u,int v,int w){
Go[++cnt]=v;Value[cnt]=w;
Next[cnt]=End[u];End[u]=cnt;
}
void Force(int l,int r){
int k=be[l];
for (int i=l;i<=r;i++){
val[i]^=1;
if (d[u[i]]>S){
F[k][id[u[i]]]^=key[v[i]];
G[k][id[u[i]]]^=key[v[i]];
}
if (d[v[i]]>S){
F[k][id[v[i]]]^=key[u[i]];
G[k][id[v[i]]]^=key[u[i]];
}
}
}
void Update(int l,int r){
int u=be[l],v=be[r];
if (u==v) return Force(l,r);
for (int i=u+1;i32767) return 1;
return 0;
}
void read(int &x){
char ch = getchar();x = 0;
for (; ch < '0' || ch > '9'; ch = getchar());
for (; ch >='0' && ch <= '9'; ch = getchar()) x = x * 10 + ch - '0';
}
LL Rand1(){return ((LL)rand()<<30LL)+rand();}
LL Rand2(){return ((LL)rand()<<45LL)+((LL)rand()<<30LL)+((LL)rand()<<15LL)+rand();}
int main(){
int Linux=Judge_Linux();
int sumn=0,summ=0,sumq=0,T;
scanf("%d",&T);
while (T--){
scanf("%d%d",&n,&m);
for (int i=1;i<=n;i++) End[i]=d[i]=0;cnt=0;
for (int i=1;i<=n;i++) key[i]=Linux?Rand1():Rand2();
for (int i=1;i<=m;i++){
read(u[i]);read(v[i]);
d[u[i]]++,d[v[i]]++;
add(u[i],v[i],i);add(v[i],u[i],i);
}
for (S=1;S*S<=m;S++);--S;tot=0;
for (int i=1;i<=m;i+=S){
tag[++tot]=0;st[tot]=i;ed[tot]=min(i+S-1,m);
for (int j=st[tot];j<=ed[tot];j++) be[j]=tot;
}
for (int i=1;i<=m;i++) val[i]=1;
*Q=0;for (int i=1;i<=n;i++)
if (d[i]>S) Q[++*Q]=i,id[i]=*Q;
for (int i=1;i<=*Q;i++){
for (int j=1;j<=tot;j++) F[j][i]=G[j][i]=0;
for (int t=End[Q[i]];t;t=Next[t])
F[be[Value[t]]][i]^=key[Go[t]];
}
int T;read(T);
sumn+=n;summ+=m;sumq+=T;
while (T--){
int opt,l,r;read(opt);read(l);read(r);
if (opt==1) Update(l,r);
else putchar((Query(l)==Query(r))+'0');
//if ((T%100==0)) fprintf(stderr,"%d\n",T);
}
puts("");
}
fprintf(stderr,"%d %d %d\n",sumn,summ,sumq);
}
更多问题,更详细题解可关注牛客竞赛区,一个刷题、比赛、分享的社区。
传送门:https://ac.nowcoder.com/acm/contest/discuss