【LOJ#10131】暗的鎖鏈
阿新 • • 發佈:2018-12-21
題目大意:給定一個 N 個點無向圖的一棵生成樹和另外 M 條邊,第一次去掉生成樹中的一條邊,第二次去掉另外 M 條邊中的一條邊,求有多少種情況可以使得給定的無向圖不連通。
題解:首先考慮該生成樹,若新增加一條邊,則會在樹上形成一個環,這時若刪除的樹邊在環上,則只有將環切斷才能使得圖不連通;若刪除的樹邊不在環上,則切斷任意一個其他邊均可。因此,只需要計算出樹上每條邊是否在環中即可。再次分析加邊過程,若在 x,y 兩點加了一條邊,則由 x,y,lca(x,y) 之間的所有邊形成了一個環,將這條樹鏈上的邊權加一即可,由此引出樹上差分做法,即:每個點記錄下該點到父節點之間的邊的環數,只需將 val[x],val[y] 加一,val[lca(x,y)] 減 2 即可,最後 dfs 求出子樹和即可。注:根節點不參與答案貢獻。
程式碼如下
#include <bits/stdc++.h> using namespace std; const int maxn=1e5+10; inline int read(){ int x=0,f=1;char ch; do{ch=getchar();if(ch=='-')f=-1;}while(!isdigit(ch)); do{x=x*10+ch-'0';ch=getchar();}while(isdigit(ch)); return f*x; } struct node{ int nxt,to; }e[maxn<<2]; int tot=1,head[maxn],val[maxn]; inline void add_edge(int from,int to){ e[++tot]=node{head[from],to},head[from]=tot; } int n,m,f[maxn][30],dep[maxn]; long long ans; void dfs(int u,int fa){ dep[u]=dep[fa]+1,f[u][0]=fa; for(int i=1;i<=20;i++)f[u][i]=f[f[u][i-1]][i-1]; for(int i=head[u];i;i=e[i].nxt){ int v=e[i].to;if(v==fa)continue; dfs(v,u); } } int lca(int x,int y){ if(dep[x]<dep[y])swap(x,y); for(int i=20;i>=0;i--)if(dep[f[x][i]]>=dep[y])x=f[x][i]; if(x==y)return x; for(int i=20;i>=0;i--)if(f[x][i]^f[y][i])x=f[x][i],y=f[y][i]; return f[x][0]; } void read_and_parse(){ n=read(),m=read(); for(int i=1,x,y;i<n;i++){ x=read(),y=read(); add_edge(x,y),add_edge(y,x); } dfs(1,0); } void calc(int u,int fa){ for(int i=head[u];i;i=e[i].nxt){ int v=e[i].to;if(v==fa)continue; calc(v,u); val[u]+=val[v]; } if(u==1)return; else if(!val[u])ans+=m; else if(val[u]==1)++ans; } void solve(){ int x,y; for(int i=1;i<=m;i++){ x=read(),y=read(); ++val[x],++val[y],val[lca(x,y)]-=2; } calc(1,0); printf("%lld\n",ans); } int main(){ read_and_parse(); solve(); return 0; }