又到了喜闻乐见的写博客清醒时间了233,今天做的依然是线段树分治
这题算是经典应用了吧,假的动态图(可离线)问题
首先不难想到对于询问的时间进行线段树分治,这样就可以把每一条边出现的时间区间扔进线段树里,考虑如何维护答案
初步的想,图上两点间异或最小值,和最大值类似。先求出一棵生成树,然后把环扔进线性基里,每次查询两点间异或值之后再扔进线性基里求最小值即可
正确性的话,因为这样环一定是有树边+非树边构成的,我们可以在任意一个点走到一个环再绕回来,中间重复走的树边因为走了两次相当于没有影响
然后我们惊喜地发现,线性基是支持撤销的,而且两点间异或值可以用带撤销并查集来做,然后线段树分治的时候回溯的时候撤销即可
然后就做完了,总复杂度(O(nlog^2 n)),足以通过此题
说句题外话,那个维护两点间距离的并查集我本来准备用我的可换根并查集来写了,但是发现换根操作的逆过程(即撤销)比较难写,所以最后不得已向差分低头
#include<cstdio>
#include<cctype>
#include<iostream>
#include<vector>
#include<utility>
#include<map>
#define RI register int
#define CI const int&
#define Tp template <typename T>
#define mp make_pair
using namespace std;
typedef pair <int,int> pi;
const int N=200005,R=30;
struct edge
{
int x,y,v,s,t;
}e[N<<1]; int n,m,q,opt,cnt,tim,x,y,qx[N],qy[N],top; map <pi,int> Hash;
class FileInputOutput
{
private:
static const int S=1<<21;
#define tc() (A==B&&(B=(A=Fin)+fread(Fin,1,S,stdin),A==B)?EOF:*A++)
#define pc(ch) (Ftop!=Fend?*Ftop++=ch:(fwrite(Fout,1,S,stdout),*(Ftop=0)++=ch))
char Fin[S],Fout[S],*A,*B,*Ftop,*Fend; int pt[15];
public:
inline FileInputOutput(void) { Ftop=Fout; Fend=Fout+S; }
Tp inline void read(T& x)
{
x=0; char ch; while (!isdigit(ch=tc()));
while (x=(x<<3)+(x<<1)+(ch&15),isdigit(ch=tc()));
}
Tp inline void write(T x)
{
RI ptop=0; while (pt[++ptop]=x%10,x/=10);
while (ptop) pc(pt[ptop--]+48); pc('
');
}
inline void flush(void)
{
fwrite(Fout,1,Ftop-Fout,stdout);
}
#undef tc
#undef pc
}F;
class Linear_Basis
{
private:
int r[100][R];
public:
inline void insert(int x,CI dep)
{
for (RI i=R-1;~i;--i) if ((x>>i)&1)
if (r[dep][i]) x^=r[dep][i]; else
{
r[dep][i]=x; for (RI j=R-1;j>i;--j)
if ((r[dep][j]>>i)&1) r[dep][j]^=x; break;
}
}
inline int query(int ret,CI dep)
{
for (RI i=R-1;~i;--i) if (r[dep][i]) ret=min(ret,ret^r[dep][i]); return ret;
}
inline void copy(CI dep)
{
for (RI i=0;i<R;++i) r[dep][i]=r[dep-1][i];
}
}LB;
class UnionFindSet
{
private:
int fa[N],size[N],val[N],stk[N];
inline int getfa(CI x)
{
return x!=fa[x]?getfa(fa[x]):x;
}
public:
inline void init(void)
{
for (RI i=1;i<=n;++i) fa[i]=i,size[i]=1;
}
inline int getval(CI x)
{
return x!=fa[x]?val[x]^getval(fa[x]):0;
}
inline int identify(CI x,CI y)
{
return getfa(x)==getfa(y);
}
inline void Union(int x,int y,CI v)
{
x=getfa(x); y=getfa(y); if (size[x]>size[y]) swap(x,y);
fa[x]=y; size[y]+=size[x]==size[y]; val[x]=v^val[x]; stk[++top]=x;
}
inline void revoke(CI tar)
{
int x; while (top>tar) x=stk[top--],size[fa[x]]-=(size[fa[x]]==size[x]+1),val[x]=0,fa[x]=x;
}
}S;
class Segment_Tree
{
private:
vector <int> pv[N<<2];
inline void expand(CI now,CI dep)
{
for (vector <int>::iterator it=pv[now].begin();it!=pv[now].end();++it)
{
int x=e[*it].x,y=e[*it].y,v=S.getval(x)^S.getval(y)^e[*it].v;
if (S.identify(x,y)) LB.insert(v,dep); else S.Union(x,y,v);
}
}
inline void calc(CI now,CI pos,CI dep,CI tp)
{
int ans=S.getval(qx[pos])^S.getval(qy[pos]);
F.write(LB.query(ans,dep)); S.revoke(tp);
}
public:
#define TN CI now=1,CI l=1,CI r=tim
#define LS now<<1,l,mid
#define RS now<<1|1,mid+1,r
inline void insert(CI beg,CI end,CI pos,TN)
{
if (beg>end) return; if (beg<=l&&r<=end) return (void)(pv[now].push_back(pos)); int mid=l+r>>1;
if (beg<=mid) insert(beg,end,pos,LS); if (end>mid) insert(beg,end,pos,RS);
}
inline void solve(TN,CI dep=1)
{
int tp=top; expand(now,dep); if (l==r) return calc(now,l,dep,tp); int mid=l+r>>1;
LB.copy(dep+1); solve(LS,dep+1); LB.copy(dep+1); solve(RS,dep+1); S.revoke(tp);
}
#undef TN
#undef LS
#undef RS
}SEG;
int main()
{
//freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout);
RI i; for (F.read(n),F.read(m),i=1;i<=m;++i)
F.read(e[i].x),F.read(e[i].y),F.read(e[i].v),
e[i].s=1,e[i].t=-1,Hash[mp(e[i].x,e[i].y)]=i;
for (cnt=m,F.read(q),i=1;i<=q;++i)
{
F.read(opt); F.read(x); F.read(y); switch (opt)
{
case 1:
e[++cnt].x=x; e[cnt].y=y; F.read(e[cnt].v);
e[cnt].s=tim+1; e[cnt].t=-1; Hash[mp(x,y)]=cnt; break;
case 2:
e[Hash[mp(x,y)]].t=tim; Hash[mp(x,y)]=0; break;
case 3:
qx[++tim]=x; qy[tim]=y; break;
}
}
if (!tim) return 0; for (i=1;i<=cnt;++i) if (!~e[i].t) e[i].t=tim;
for (i=1;i<=cnt;++i) SEG.insert(e[i].s,e[i].t,i);
return S.init(),SEG.solve(),F.flush(),0;
}