luogu 3733 八纵八横
[HAOI2017]八纵八横
1 题目描述
-
Anihc 国有 n个城市,这 n个城市从 1到 n编号,1号城市为首都。城市间初始时有 m条高速公路,每条高速公路都有一个非负整数的经济影响因子,每条高速公路的两端都是城市(可能两端是同一个城市),保证任意两个城市都可以通过高速公路互达。
国正在筹划“八纵八横”的高铁建设计划,计划要修建一些高速铁路,每条高速铁路两端也都是城市(可能两端是同一个城市),也都有一个非负整数的经济影响因子。国家还计划在“八纵八横”计划建成之后,将“一带一路”扩展为“一带_路一环”,增加“内陆城市经济环”即选择一条从首都出发沿若一系列高铁与高速公路走的路径,每条高铁或高速公路可以经过多次,每座城市也可以经过多次,最后路径又在首都结束。令“内陆城市经济环”的 GDP 为依次将这条路径上所经过的高铁或高速公路的经济影响因子异或起来(一条路经过多次则会被计算多次)。
现在 Anihc 在会议上讨论“八纵八横”的建设计划方案,他们会不断地修改计划方案,希望你能实时反馈对于当前的“八纵八横”的建设计划的方案“内陆城市经济环”的最大是多少。
初始时,八纵八横计划中不包含任何一条高铁,有以下 3 种操作
Add x y z
在计划中给在城市 x和城市 y之间建设一条高铁,其经济影响因子为 z,如果这是第 k个
Add
操作,则将这条高铁命名为 k号高铁。Cancel k
将计划中的 kk 号高铁取消掉,保证此时 k号高铁一定存在
Change k z
表示将第 k 号高铁的经济影响因子更改为 z,保证此时 k 号高铁一定存在。
2 分析
- 本题类似于wc2011那题最大路径异或和,但是本题求的是环。其实本题我们只要找出所有环异或的最大值就可以了,即使这个结果没有从1开始,我们仍旧可以从1开始走一条来回的路到答案那边,这样对我们之前算出来的环的异或值没有任何影响。
- 如果我们直接暴力,这样可以得到70分。时间复杂度\(O(\frac{qmL^2}{128})\),q是操作的次数,m是每次灌水找环的时间复杂度,\(L^2\)是建立线性基的复杂度,128是bitset的常数。
- 由于最开始的边一定不会被删除,这样就保证了操作的过程中始终是一棵树,我们可以先随便求出这棵树的生成树,对于每次加边的会产生环的问题,就可以在树上直接产生环。有人可能认为,这样的环是不是少了,因为这样好像只会产生小环,那些更大的环没有产生。其实这是没关系的,因为大环可以通过小环的异或得到。
- 由于线性基不能支持删除,所以我们可以删除和修改操作去掉,我们可以利用线段树分治把删除和修改操作去掉。把如果在第i个时刻插入,在第j个时刻删除,那么我们就让这条边在第i时刻到第j-1时刻有效。如果在第i个时候修改了某条边,那么我们就把这条的有效范围一分为二。
- 线段树分治+线性基+bitset的时间复杂度:\(\frac{q\log_{2}{q}\times L^2}{128}\)。
3 代码
-
#include<bits/stdc++.h> using namespace std; int const N=1000+5; #define lc (o<<1) #define rc (o<<1|1) #define mid (l+r)/2 typedef bitset<N> bs; struct edge{ int x,y,l,r; bs z; }e[N<<1]; int n,m,q,k,f[N],mx,pos[N],cnt; bs t[20][N],val[N]; vector<int> se[N<<2]; vector<int> mat[N]; vector<bs> E[N]; int find(int x){ return x==f[x]? x:f[x]=find(f[x]); } void update(int o,int l,int r,int ll,int rr,int x){ if(ll<=l && r<=rr) { se[o].push_back(x); return ; } if(ll<=mid) update(lc,l,mid,ll,rr,x); if(rr>mid) update(rc,mid+1,r,ll,rr,x); } void write(bs d[]){ bs ans; for(int i=mx-1;i>=0;i--) if(!ans[i]) ans=ans^d[i]; int check=0; for(int i=mx-1;i>=0;i--){ if(ans[i]) check=1; if(check) putchar(48+ans[i]) ; } printf("\n"); } void ins(bs d[],bs x){ for(int i=mx-1;i>=0;i--) if(x[i]){ if(d[i][i]) x=x^d[i]; else { d[i]=x; return ; } } } void query(int o,int l,int r,int d){ for(int i=0;i<mx;i++) if(t[d-1][i][i]) t[d][i]=t[d-1][i]; for(int i=0;i<se[o].size();i++) ins(t[d],e[se[o][i]].z^val[e[se[o][i]].x]^val[e[se[o][i]].y]); if(l==r) write(t[d]); else { query(lc,l,mid,d+1); query(rc,mid+1,r,d+1); } for(int i=0;i<mx;i++) if(t[d][i][i]) t[d][i].reset(); } void dfs(int x,bs t,int fa){ val[x]=t; for(int i=0;i<mat[x].size();i++) { int v=mat[x][i]; if(v!=fa) dfs(v,t^E[x][i],x); } } int main(){ cin>>n>>m>>q; for(int i=1;i<=n;i++) f[i]=i; while (m--){ int x,y;string z; cin>>x>>y>>z; mx=max(mx,(int)z.size()); if(find(x)==find(y)) e[++k]=(edge){x,y,0,q,bs(z)}; else { f[find(x)]=find(y); E[x].push_back(bs(z)); E[y].push_back(bs(z)); mat[x].push_back(y); mat[y].push_back(x); } } for(int i=1;i<=q;i++){ char s[10]; scanf("%s",s); if(s[0]=='A') { int x,y;string z; cin>>x>>y>>z; mx=max(mx,(int)z.size()); e[++k]=(edge){x,y,i,q,bs(z)}; pos[++cnt]=k; } if(s[1]=='a'){ int x; cin>>x; e[pos[x]].r=i-1; pos[x]=0; } if(s[1]=='h'){ int x; string z; cin>>x>>z; e[pos[x]].r=i-1; e[++k]=e[pos[x]]; e[k].l=i; e[k].r=q; e[k].z=bs(z); pos[x]=k; } } bs t; dfs(1,t,1); for(int i=1;i<=k;i++){ update(1,0,q,e[i].l,e[i].r,i); } query(1,0,q,1); return 0; }