这是qzh的第二题
题目大意:
给你一棵树和一些连接祖先和孩子的边(非树枝边,类似于有向图返祖边)
让你求出删掉其中一条树枝边和一条非树枝边使图不联通的方案数
我们思考对树枝边统计答案
如图
对于红边统计答案,它的子树中有一条向外连的边,必须删掉红边和这条边才能使图不连通,所以这条树枝边贡献为1
如图
对于红边统计答案,它的子树中有0条向外连的边,删掉红边和任意一条虚线边能使图不连通,所以这条树枝边贡献为非树枝边的数量
如图
对于红边统计答案,它的子树中有两条及以上向外连的边,删掉红边和任意一条虚线边不能使图不连通,所以这条树枝边贡献为0
/-----------------------------------------------------------------/
那么问题就转化为求子树中有多少向外连的边,那就树上差分一下,把非树枝边连接的两个点中,深度较深的点标记+1,深度较浅的点标记-1,统计一个点的向外连的边就是统计子树中标记和.
然后就非常简单
代码:
%:pragma GCC optimize(3) #include<cstdio> #include<vector> using namespace std; const int N=4000010,M=8000010,P=24; long long ans; int fi[N],ne[M],b[M],deep[N],value[N],n,m,k,x,y,flag; vector<int>e1,e2; void add(int x,int y){ b[++k]=y; ne[k]=fi[x]; fi[x]=k; } void dfs(int x,int fa){ deep[x]=deep[fa]+1; for (int j=fi[x]; j; j=ne[j]) if (b[j]!=fa) dfs(b[j],x); } int getans(int x,int fa){ int res=value[x]; for (int j=fi[x]; j; j=ne[j]) if (b[j]!=fa){ int k=getans(b[j],x); if (k==0) ans+=e1.size(); else if (k==1) ans++; res+=k; } return res; } int main(){ scanf("%d%d",&n,&m); for (int i=1; i<=m; i++){ scanf("%d%d%d",&x,&y,&flag); if (flag==1) add(x,y),add(y,x); else e1.push_back(x),e2.push_back(y); } dfs(1,1); for (int i=0; i<e1.size(); i++){ x=e1[i]; y=e2[i]; if (deep[x]>deep[y]) x^=y^=x^=y; value[y]++,value[x]--; } getans(1,1); printf("%lld",ans); return 0; }
/----------------------------------------------------------------/
想必到这里都非常简单,那么如果,非树枝边可以连接两个没有祖先和子孙关系的点呢?
没区别,只要对这种边打标记时,对与连接的两个点标记+1,他们的lca标记-1即可
时间: 2024-11-07 17:16:45