感谢博主:https://www.luogu.com.cn/blog/116113X/solution-p2542
题意:略:
错误解法:这个题我一开始想的是持续动态更新Tarjan双联通缩点后找桥,但是显然从理论上来说复杂度过不去,所以考虑更换思路
正确解法:这个时候我去搜了一下题解,感觉这个题很不错。因为是双向建边,所以可以用树链剖分来做(LCT没学过不会qwq)。我们先用并查集来保证成树(树上所有的边显然都是关键边),我们把关键边的边权设置为1,这个时候还有一些边是多余的(日后也不会删除),但是我们知道一旦一棵树已经是树了(诶,好绕口哈哈),如果再在树上任意2个点直接添加一条新的边,那么这2个点势必会成为环(换句话说就是这2个点之间就会没有关键边)。那么OK,我们继续想,考虑怎么把多余且日后不会被删的边添加进去。因为此时建成的树是全部都是关键边无环的,所以更新多余边的时候我们就用树链剖分来搞,那些多余的边边权设置为0,把这2个点之间的边权值全部覆盖成0,这时候修改。那么一棵有新的边权的树就更新建成啦。
接下来就是考虑删边操作。但是正向删边不行,因为删掉一条边又保证联通,显然只能删环上的边,即每次删边后关键边数量一定不减,(假设删边(u,v))若我们每次在u到v的路径上加1,显然无法最优,因为对于u到v路径上的边,不一定删掉(u,v)这条边就一定会让它变成关键边,有可能它还属于别的环。所以考虑用反向操作。我们知道一开始更新完的图就是等同于全部删边删完后的情况,所以我们每次选择反向更新,一开始所有要删掉的边都先不加,相当于默认全部删完了,倒序查询询问后再一条一条加回去,这样就可以避免删边操作了。。把正向删边转换成反向添边。添边操作和上段一样就是把这2个点之前的边权值覆盖为0就ok。然后查询的时候同理用qchain查询就行。
细节:注意啦。树链剖分维护的是点权值,我们这道题要处理的是边权值,所以树剖板子得改一下(99%不用改),就是qchain和mchain最后那一行dfn[x]需要+1。因为比如我们求x-y之间的边权和,边的数量其实是x-y之间点的数量(包括x和y)再减去1。我们求和或者更新的时候,可以不把dfn[x]考虑在内,就是区间范围去掉dfn[x]这样。

#include<bits/stdc++.h> #define ll long long #define rep(i,a,n) for(int i=a;i<=n;i++) #define per(i,n,a) for(int i=n;i>=a;i--) #define endl ' ' #define eps 0.000000001 #define pb push_back #define mem(a,b) memset(a,b,sizeof(a)) #define IO ios::sync_with_stdio(false);cin.tie(0); using namespace std; const int INF=0x3f3f3f3f; const ll inf=0x3f3f3f3f3f3f3f3f; const int mod=1e9+7; const int maxn=1e5+5; int tot,head[maxn]; struct E{ int to,next; }edge[maxn<<1]; void add(int u,int v){ edge[tot].to=v; edge[tot].next=head[u]; head[u]=tot++; } struct nod{ int t,a,b; }dt[maxn]; int n,m,fa[maxn]; int find(int x){ while(x!=fa[x]) x=fa[x]=fa[fa[x]]; return x; } int uu[maxn],vv[maxn],vis[maxn]; map<pair<int,int>,int>mp; int v[maxn],root; int dep[maxn],siz[maxn],son[maxn]; void dfs1(int u,int f){ fa[u]=f; dep[u]=dep[f]+1; siz[u]=1; int maxsize=-1; for(int i=head[u];i!=-1;i=edge[i].next){ int v=edge[i].to; if(v==f) continue; dfs1(v,u); siz[u]+=siz[v]; if(siz[v]>maxsize){ maxsize=siz[v]; son[u]=v; } } } int tim,dfn[maxn],top[maxn],w[maxn]; void dfs2(int u,int t){ dfn[u]=++tim; top[u]=t; if(!son[u]) return ; dfs2(son[u],t); for(int i=head[u];i!=-1;i=edge[i].next){ int v=edge[i].to; if(v==fa[u]||v==son[u]) continue; dfs2(v,v); } } struct node{ int l,r,sum,lz; }tree[maxn<<2]; inline void build(int i,int l,int r){ tree[i].l=l;tree[i].r=r,tree[i].lz=-1; if(l==r){ tree[i].sum=1; return ; } ll mid=(l+r)>>1; build(2*i,l,mid); build(2*i+1,mid+1,r); tree[i].sum=tree[2*i].sum+tree[i*2+1].sum; } inline void push_down(int i){ if(tree[i].lz!=-1){ tree[i*2].lz=tree[i].lz; tree[i*2+1].lz=tree[i].lz; ll mid=(tree[i].l+tree[i].r)/2; tree[i*2].sum=tree[i].lz*(mid-tree[i*2].l+1); tree[i*2+1].sum=tree[i].lz*(tree[i*2+1].r-mid); tree[i].lz=-1; } return ; } inline void modify(int i,int l,int r,int k){ if(tree[i].r<=r && tree[i].l>=l){ tree[i].sum=k*(tree[i].r-tree[i].l+1); tree[i].lz=k; return ; } push_down(i); if(tree[i*2].r>=l) modify(i*2,l,r,k); if(tree[i*2+1].l<=r) modify(i*2+1,l,r,k); tree[i].sum=tree[i*2].sum+tree[i*2+1].sum; } inline int query(int i,int l,int r){ if(tree[i].l>=l && tree[i].r<=r) return tree[i].sum; if(tree[i].r<l || tree[i].l>r) return 0; push_down(i); ll s=0; if(tree[i*2].r>=l) s+=query(i*2,l,r); if(tree[i*2+1].l<=r) s+=query(i*2+1,l,r); return s; } void mchain(int x,int y,int z){ while(top[x]!=top[y]){ if(dep[top[x]]<dep[top[y]]) std::swap(x,y); modify(1,dfn[top[x]],dfn[x],z); x=fa[top[x]]; } if(dep[x]>dep[y]) std::swap(x,y); modify(1,dfn[x]+1,dfn[y],z); } int qchain(int x,int y){ int ret=0; while(top[x]!=top[y]){ if(dep[top[x]]<dep[top[y]]) std::swap(x,y); ret+=query(1,dfn[top[x]],dfn[x]); x=fa[top[x]]; } if(dep[x]>dep[y]) std::swap(x,y); ret+=query(1,dfn[x]+1,dfn[y]); return ret; } int main(){ scanf("%d%d",&n,&m);mem(head,-1); rep(i,1,m){ scanf("%d%d",&uu[i],&vv[i]); } int cnt=0; while(1){ int op;scanf("%d",&op); if(op==-1) break; int a,b;scanf("%d%d",&a,&b); dt[++cnt]={op,a,b}; if(!op) mp[{a,b}]=mp[{b,a}]=1; } rep(i,1,n) fa[i]=i; rep(i,1,m){ int u=find(uu[i]),v=find(vv[i]); if(!mp[{uu[i],vv[i]}]&&fa[u]!=v){ fa[u]=v; add(uu[i],vv[i]); add(vv[i],uu[i]); vis[i]=true; } } mem(fa,0); dfs1(1,-1); dfs2(1,1); build(1,1,n); rep(i,1,m){ if(!vis[i]&&!mp[{uu[i],vv[i]}]){ int a=uu[i],b=vv[i]; mchain(a,b,0); } } stack<int> q; for(int i=cnt;i>=1;i--){ int ans=0,t=dt[i].t,a=dt[i].a,b=dt[i].b; if(t){ ans=qchain(a,b); q.push(ans); }else{ mchain(a,b,0); } } while(!q.empty()){ int now=q.top(); cout<<now<<endl; q.pop(); } }