题解一本通1553:例 2暗的连锁
Posted zhengkunjia
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了题解一本通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
*/
以上是关于题解一本通1553:例 2暗的连锁的主要内容,如果未能解决你的问题,请参考以下文章
AcWing 352/loj 10131. 「一本通 4.4 例 2」暗的连锁