【题解】一本通1553:【例 2】暗的连锁

[题目](http://ybt.ssoier.cn:8088/problem_show.php?pid=1553)

前置知识:倍增求LCA(当然不倍增也行)

1553:【例 2】暗的连锁

时间限制: 1000 ms 内存限制: 524288 KB
提交数: 456 通过数: 201

【题目描述】

原题来自:POJ 3417

Dark 是一张无向图,图中有 NN 个节点和两类边,一类边被称为主要边,而另一类被称为附加边。Dark 有 N–1N–1 条主要边,并且 Dark 的任意两个节点之间都存在一条只由主要边构成的路径。另外,Dark 还有 MM 条附加边。

你的任务是把 Dark 斩为不连通的两部分。一开始 Dark 的附加边都处于无敌状态,你只能选择一条主要边切断。一旦你切断了一条主要边,Dark 就会进入防御模式,主要边会变为无敌的而附加边可以被切断。但是你的能力只能再切断 Dark 的一条附加边。

现在你想要知道,一共有多少种方案可以击败 Dark。注意,就算你第一步切断主要边之后就已经把 Dark 斩为两截,你也需要切断一条附加边才算击败了 Dark。

【输入】

第一行包含两个整数 NN 和 MM;

之后 N–1N–1行,每行包括两个整数 AA 和 BB,表示 AA 和 BB 之间有一条主要边;

之后 MM 行以同样的格式给出附加边。

【输出】

输出一个整数表示答案。

【输入样例】

4 1
1 2
2 3
1 4
3 4

【输出样例】

3

【提示】

数据范围与提示:

对于 20% 的数据,1≤N,M≤1001≤N,M≤100;

对于 100% 的数据,1≤N≤105,1≤M≤2×1051≤N≤105,1≤M≤2×105 。数据保证答案不超过 231−1231−1。
(看数据会发现:两点之间直接相连的附加边可能有很多条;自己与自己也能连接一条附加边)

题解

附加边:

添加一条附加边,不仅会造成这条边直接相连的两个点之间的主要边受阻,还会造成其他的一些边受阻
这里的受阻,是指砍除两点之间的主要边之后,必须再砍除这条附加边,才能将树分成两段

受阻范围:

就是从附加边连接的两点,到它们的LCA之间的所有边。也就是这两点之间的最短路径

贡献解的条件:

若砍除一条主要边之后,再砍掉它受阻的附加边,能将原树分成两半,那么这条边受阻的附加边必然不大于2
所以我们要记录每一条主要边受几条附加边的阻碍

怎么记录?:

只要这条边在附加边阻碍范围的最短路径上,也就是在lca为根的树上,就会受阻;但一旦这条边出了lca子树,便不受阻。可以理解为在lca子树里,lca和附加边造成的阻碍相互抵消了。
因此我们可以利用差分的思想

变量和函数

k[]&&cha(x,y):一条附加边连接x和y,它们的LCA用函数find_LCA(x,y)找到,那么将k[x]和k[y]都加一,将k[find_LCA(x,y)]减二,就实现了差分
quan[]&&get_quan():对于连接点u和它的父亲fa的边x,计算出以u为根的子树的所有节点的k[]之和,就是边x所受的阻碍次数(这个不难想诶,,,)
get_LCA&&f[][]&&dep[]:求LCA嘛,不多说

方案数

唉,交了三遍才发现题目让求的是方案数,不是主要边的数量。。。
如果这条主要边受阻0次,那砍完这条主要边后,从所有的附加边中随便砍一条,答案加m;
如果它受阻1次,那砍完它后必须再砍断阻碍它的附加边,答案加1
所以:code:

#include <cmath>
#include <cstdio>
#include <string>
#include <cstring>
#include <cstdlib>
#include <iostream>
#include <algorithm>
#define F(i,a,b) for(int i=a;i<=b;i++)
#define UF(i,a,b) for(int i=a;i>=b;i--)
#define N 100010
#define M 200010
using namespace std;
int n, m, next[N], fir[N], to[N], len, k[N], quan[N], x, y, f[N][25], ans, dep[N];
void ins(int x, int y)
{
	len++;
	next[len]=fir[x];
	to[len]=y;
	fir[x]=len;
	/*len++;
	next[len]=fir[y];
	to[len]=x;
	fir[y]=len;*/
}
void deal_first(int u, int fa)
{
	dep[u]=dep[fa]+1;
	F(i,0,19){
		f[u][i+1]=f[f[u][i]][i];
	}
	for(int e=fir[u];e;e=next[e]){
		int v=to[e];
		f[v][0]=u;
//		cout << v << " ";
		deal_first(v,u);
	}//cout << endl;
}
int find_LCA(int x, int y)
{
	if(x==y){
		return x; 
	}
	if(dep[x]<dep[y])	swap(x,y);
	UF(i,20,0){
		if(dep[f[x][i]]>=dep[y])	x=f[x][i];
		if(x==y){
			return x; 
		}
	}
	UF(i,20,0){
		if(f[x][i]!=f[y][i]){
			x=f[x][i];
			y=f[y][i];
		}
	}
	return f[x][0]; 
}
void cha(int x, int y)
{
	k[x]+=1;
	k[y]+=1;
	k[find_LCA(x,y)]-=2;
	return;
}
void get_quan(int x, int u, int fa)
{
//	cout << "*";
	if(quan[x] != N)	return;
	quan[x]=0;
	for(int e=fir[u];e;e=next[e]){
		int v=to[e];
		get_quan(e,v,u);
		quan[x]+=quan[e];
	}
	quan[x]+=k[u];
	if(quan[x]<=1 && x!=0){
		if(quan[x]==0)	ans+=m;
		if(quan[x]==1)	ans+=1;
	}
	return;
}

void check()
{
	printf("len=%d
",len);
	F(i,1,n){
		for (int e=fir[i];e;e=next[e])
		{
			cout << to[e] << " ";
		}cout << endl;
	}
}void check02()
{
	F(i,1,n)	cout<<dep[i]<<" ";
	cout << endl;
	F(i,1,n){
		F(j,0,2)
			cout << f[i][j] << " ";
		cout << endl;
	}
}
void check03()
{
	F(i,1,n)	cout << k[i] << " ";
	cout << endl;
}
void check04()
{
	F(i,0,len)	cout << quan[i] << " ";
	cout << endl;
}
int main()
{
	scanf("%d%d",&n,&m);
	F(i,1,n-1){
		scanf("%d%d",&x,&y);
		ins(x,y);
	}
	deal_first(1,0);
	F(i,1,m){
		scanf("%d%d",&x,&y);
		cha(x,y);
	}
	F(i,0,len)	quan[i]=N;
//	check();
//	check02();
//	check03();
	get_quan(0,1,0);
//	check04();
	printf("%d",ans);
	return 0;
}
/*
7 3
1 2
1 7 
2 3
2 4
2 5
3 6
3 4
4 5
4 6
*/
原文地址:https://www.cnblogs.com/ZhengkunJia/p/12275722.html