暗的连锁

题目链接

算法:

         我们可以用树上差分的方法,求出一个非根节点有多少条路径到达他父亲(除了从那条直接连接的主要边),如果没有这样的路径,那么割掉这条主要边,然后去掉任意一个附加边即可,对答案贡献为m;如果有一条这样的路径,那么我们可以先去掉直接连接的边,然后断掉这一条通路即可,对答案贡献为1;如果这样的路径个数大于1,那么我们对于割这条主要边,是不存在任何一个方案满足答案的。直接模拟统计即可。

Code:

#include<iostream>
#define rep(i,j,k) for(int i=j;i<=k;i++)
#define rep2(i,j,k) for(int i=j;i>=k;i--)
using namespace std;
template<typename T> void read(T &num){scanf("%d",&num);}
template<typename T> void write(T x){printf("%d\n",x);}
int n,m;
struct wzy{
	int nxt,vertice;
}edge[200010];
int head[100010];int len=0;
inline void add_edge(int x,int y){
	edge[++len].nxt=head[x];edge[len].vertice=y;head[x]=len;return;
}

long long ans=0;
int dep[100010];int f[100010][17];int dif[100010];
inline void Pretreatment(int son,int father){
	dep[son]=dep[father]+1;f[son][0]=father;
	rep(i,1,16){f[son][i]=f[f[son][i-1]][i-1];}
	for(int i=head[son];i;i=edge[i].nxt){
		int nop=edge[i].vertice;
		if(nop==father)continue;
		Pretreatment(nop,son);
	}
	return;
}
inline int LCA(int x,int y){
	if(dep[x]<dep[y])swap(x,y);
	rep2(i,16,0){
		if(dep[f[x][i]]>=dep[y]){x=f[x][i];}
	}
	if(x==y)return x;
	rep2(i,16,0){
		if(f[x][i]!=f[y][i]){x=f[x][i];y=f[y][i];}
	}
	return f[x][0];
}
inline void solve(int son,int father){
	for(int i=head[son];i;i=edge[i].nxt){
		int nop=edge[i].vertice;
		if(nop==father)continue;
		solve(nop,son);dif[son]+=dif[nop];
	}
	if(son==1)return;
	ans+=m*(dif[son]==0)+(dif[son]==1);
	return;
}

int main(){
	read(n);read(m);
	rep(i,1,n-1){int x,y;read(x);read(y);add_edge(x,y);add_edge(y,x);}
	Pretreatment(1,0);
	
	rep(i,1,m){int x,y;read(x);read(y);dif[x]++;dif[y]++;dif[LCA(x,y)]-=2;}
	
	solve(1,0);write(ans);
	return 0;
}

猜你喜欢

转载自blog.csdn.net/Bill_Benation/article/details/88198731