poj3417lca+树上差分

it2022-05-05  97

/* 给定n个点的树,在其中加入m条新边(称为非树边) 现在可以割断一条树边,一条非树边,使图分裂成两个联通块,请问有几种切割方式 对树边进行分情况讨论 如果树边不处在环中,则割断这条树边后可以割断任意条非树边 如果树边仅仅被一个环包含,则割断这条树边后只能割断一条非树边,即环中的那条非树边 如果树边被两个及以上环包含,就不可能有合法的切割方式 那么考虑如何计算树边被多少个环包含 显然每次加入一条非树边(x,y),x->lca(x,y)->y->x就会形成一个环 如果第一次割断这个环上的边,那么第二次就必须割断(x,y) 注意这里的环仅仅指的是非树边沿着两段向上覆盖的环 那么每次加入一条非树边(x,y),就找到lca(x,y),x->lca->y路径上的所有边权+1 如何将路径上所有边权+1:使用树上差分即可,仅仅操作lca,x,y三个点的权值即可 最后枚举每一条边,分类考虑边权=0,1,2的三种情况并求和即可 */ #include<iostream> #include<cstring> #include<cstdio> #include<queue> #include<cmath> using namespace std; #define maxn 200005 struct Edge{int to,nxt;}edge[maxn<<1]; int head[maxn],tot,n,m,cnt[maxn]; void init(){ memset(head,-1,sizeof head); tot=0; } void addedge(int u,int v){ edge[tot].to=v;edge[tot].nxt=head[u];head[u]=tot++; } int f[maxn][30],d[maxn],t; void bfs(){ queue<int>q; q.push(1);d[1]=1; while(!q.empty()){ int u=q.front();q.pop(); for(int i=head[u];i!=-1;i=edge[i].nxt){ int v=edge[i].to; if(d[v])continue; f[v][0]=u; d[v]=d[u]+1; for(int k=1;k<=29;k++) f[v][k]=f[f[v][k-1]][k-1]; q.push(v); } } } int lca(int x,int y){ if(d[x]<d[y])swap(x,y); for(int i=29;i>=0;i--) if(d[f[x][i]]>=d[y])x=f[x][i]; if(x==y)return y; for(int i=29;i>=0;i--) if(f[x][i]!=f[y][i])x=f[x][i],y=f[y][i]; return f[x][0]; } long long ans; void dfs(int u,int pre){ for(int i=head[u];i!=-1;i=edge[i].nxt){ int v=edge[i].to; if(v==pre)continue; dfs(v,u); cnt[u]+=cnt[v]; } } int main(){ init(); cin>>n>>m; for(int i=1;i<n;i++){ int u,v; scanf("%d%d",&u,&v); addedge(u,v);addedge(v,u); } bfs(); for(int i=1;i<=m;i++){ int u,v; scanf("%d%d",&u,&v); cnt[lca(u,v)]-=2; cnt[u]++;cnt[v]++; } dfs(1,0); for(int i=2;i<=n;i++) if(cnt[i]==0)ans+=m; else if(cnt[i]==1)ans++; cout<<ans<<endl; }

 

转载于:https://www.cnblogs.com/zsben991126/p/10511058.html

相关资源:各显卡算力对照表!

最新回复(0)