题意:给你一颗生成树,然后有一些额外的边连接在生成树上的两点,问你最少要割几条边才能使图不连通,割的边中只能有一条生成树上的边,不能少也不能多
思路:训练没出,现在来补坑。我们稍加思考可以想到对于一颗生成树,我们其余的边相当于覆盖u,v之前的路线,如果你要割u,v之间的生成树的边的话就要多割一条,我们很直接的想到用线段树来维护区间操作,但是这个题在我们训练的时候是会超时的,所以我们改用树差分,因为我们是树链剖分的,树链剖分有一条性质就是我们在剖分的时候重链的编号是连续的,就是这一条性质保证了可以树差分。
代码:
#include <iostream> #include <cstdio> #include <cstring> using namespace std; const int maxn=500008; int a[maxn],sz[maxn],dep[maxn],fa[maxn],top[maxn],son[maxn],head[maxn]; int w[maxn],num,cnt; int c[maxn]; struct Edge { int to,next; }; Edge v[maxn*2]; void init() { num=0;cnt=0; memset(head,-1,sizeof(head)); } void addedge(int x,int y) { v[cnt].to = y; v[cnt].next = head[x]; head[x] = cnt++; } void dfs1(int u, int f, int d) { dep[u]=d;sz[u]=1; son[u]=0;fa[u]=f; for(int i=head[u];i!=-1;i = v[i].next){ int to = v[i].to; if(to!=f){ dfs1(to,u,d+1); sz[u]+=sz[to]; if(sz[son[u]]<sz[to])son[u]=to; } } } void dfs2(int u, int tp) { top[u]=tp; w[u]=++num; if(son[u])dfs2(son[u],tp); for(int i = head[u];i!=-1;i=v[i].next){ int to=v[i].to; if(to==fa[u]||to==son[u])continue; dfs2(to,to); } } void modify(int x,int y) { while(top[x]!=top[y]){ if(dep[top[x]]<dep[top[y]])swap(x,y); c[w[top[x]]]++; c[w[x]+1]--; x=fa[top[x]]; } if(x==y)return ; if(dep[x]>dep[y])swap(x,y); c[w[son[x]]]++;c[w[y]+1]--; return ; } int main() { int t,n,m,cas=0; scanf("%d",&t); while(t--){ init(); scanf("%d%d",&n,&m); int a,b; for(int i=1;i<=n-1;i++){ scanf("%d%d",&a,&b); addedge(a,b); addedge(b,a); } dfs1(1,0,1); dfs2(1,1); memset(c,0,sizeof(c)); for(int i=1;i<=m-n+1;i++){ scanf("%d%d",&a,&b); modify(a,b); } for(int i=1;i<=n;i++){ c[i]+=c[i-1]; } int ans=0x3f3f3f3f; for(int i=1;i<=n;i++){ if(c[i]!=0&&c[i]){ ans=min(ans,c[i]); } } printf("Case #%d: %d ",++cas,ans+1); } return 0; }