CF938G Shortest Path Queries 解题报告
给出一个连通带权无向图,边有边权,要求支持 \(q\) 个操作:
1 x y d
在原图中加入一条 \(x\) 到 \(y\) 权值为 \(d\) 的边
2 x y
把图中 \(x\) 到 \(y\) 的边删掉
3 x y
表示询问 \(x\) 到 \(y\) 的异或最短路\(n,m,q\le 200000\)
一道很经典的线段树分治题目,可以将询问按时间建出下标线段树,将修改的添加和删除看做对于一段时间区间的修改。
像一般的线段树区间操作的方法,每个节点存下懒标记,再遍历整棵线段树。
关于没有修改的做法,具体可看 [WC2011]最大XOR和路径 解题报告 ,对于当前图求出线性基,再随便找一个异或路径放进去比较。
对于每个询问时图的线性基,根据线性基的优秀性质,存储和传递的复杂度都十分小,可以在当前节点接受父亲的线性基后在将懒标记里的边依次插入,然后继续下传一直到叶子节点。
至于异或路径,可以通过可撤销的并查集做到 \(\mathcal{O(n\log n)}\) ,注意到了叶子节点时也要撤回叶子节点懒标记的边的插入,不然就会像我一样调一个月😥
最后来个 双倍经验 。
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int M=1e6+5;
int min(int x,int y){ return x<y?x:y; }
int max(int x,int y){ return x>y?x:y; }
void swap(int &x,int &y){ return (void)(x^=y^=x^=y); }
int n,m,q;
int read(){
int x=0,y=1;char ch=getchar();
while(ch<'0'||ch>'9') y=(ch=='-')?-1:1,ch=getchar();
while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
return x*y;
}
int tot=0,num=0;
struct Edge{
int x,y,z;
}e[M];
struct Query{
int x,y;
}Q[M];
int p[M][35];
void insert(int x,int pos){
for(int i=30;i>=0;i--){
int c=(x>>i)&1;
if(!c) continue ;
if(p[pos][i]) x^=p[pos][i];
else return (void)(p[pos][i]=x);
}
}
int query(int x,int pos){
for(int i=30;i>=0;i--){
int c=(x>>i)&1;
// if(!c||!p[pos][i]) continue ;
// x^=p[pos][i];
if((x^p[pos][i])<x) x^=p[pos][i];
}
return x;
}
void copy(int x,int y){ for(int i=30;i>=0;i--) p[y][i]=p[x][i]; }
int cnt=0,fa[M],de[M],dis[M];
struct BCJ{
int x,y,dep;
}f[M];
int find(int x){ return fa[x]==x?fa[x]:find(fa[x]); }
int Xord(int x){ return fa[x]==x?dis[x]:dis[x]^Xord(fa[x]); }
void Merge(int x,int y,int z){
if(de[x]<de[y]) swap(x,y);
f[++cnt]=(BCJ){x,y,de[x]};
fa[y]=x,dis[y]=z,de[x]=max(de[x],de[y]+1);
}
void Del(int id){
int x=f[id].x,y=f[id].y,dep=f[id].dep;
fa[y]=y,dis[y]=0,de[x]=dep;
}
vector<int> lazy[M<<2];
void Insert(int u,int l,int r,int L,int R,int id){
if(L>r||R<l) return ;
if(L<=l&&R>=r) return (void)(lazy[u].push_back(id));
int mid=(l+r)>>1;
Insert(u<<1,l,mid,L,R,id),Insert(u<<1|1,mid+1,r,L,R,id);
}
void Get_Ans(int u,int l,int r){
int now=cnt,sz=lazy[u].size();
for(int i=0;i<sz;i++){
int id=lazy[u][i],x=e[id].x,y=e[id].y,z=e[id].z;
z^=Xord(x)^Xord(y),x=find(x),y=find(y);
if(x==y) insert(z,u);
else Merge(x,y,z);
}
if(l==r){
int x=Q[l].x,y=Q[l].y;
if(find(x)!=find(y)) printf("%lld\n",0);
else printf("%lld\n",query(Xord(x)^Xord(y),u));
}
else{
int mid=(l+r)>>1;
copy(u,u<<1),Get_Ans(u<<1,l,mid);
copy(u,u<<1|1),Get_Ans(u<<1|1,mid+1,r);
}
while(cnt>now) Del(cnt--);
}
int pre[M],suf[M];
map<pair<int,int>,int> mmp;
void solve(){
n=read(),m=read();tot=m;
for(int i=1;i<=n;i++) fa[i]=i,de[i]=dis[i]=0;
for(int i=1;i<=m;i++){
e[i].x=read(),e[i].y=read(),e[i].z=read();
if(e[i].x>e[i].y) swap(e[i].x,e[i].y);
mmp[make_pair(e[i].x,e[i].y)]=mmp[make_pair(e[i].y,e[i].x)]=i;
pre[i]=1,suf[i]=-1;
}q=read();
while(q--){
int opt=read();
if(opt==1){
e[++tot].x=read(),e[tot].y=read(),e[tot].z=read();
if(e[tot].x>e[tot].y) swap(e[tot].x,e[tot].y);
mmp[make_pair(e[tot].x,e[tot].y)]=mmp[make_pair(e[tot].y,e[tot].x)]=tot;
pre[tot]=num+1,suf[tot]=-1;
}
else if(opt==2){
int x=read(),y=read();
if(x>y) swap(x,y);
suf[mmp[make_pair(x,y)]]=num;
}
else if(opt==3) Q[++num].x=read(),Q[num].y=read();
}
for(int i=1;i<=tot;i++){
if(suf[i]==-1) suf[i]=num;
if(suf[i]>=pre[i]) Insert(1,1,num,pre[i],suf[i],i);
}
Get_Ans(1,1,num);
}
signed main(){
// freopen("shuju.in","r",stdin);
// freopen("std.out","w",stdout);
solve();
}