不是,第一次被一道黄题卡了一个小时……
题目传送门
基环树
这是一棵树,它的本质就是一张没有环的图,有
n
n
n个点和
n
−
1
n-1
n−1条边。
这是一颗基环树,就是再一棵树上面加了一条边,所以它有
n
n
n个点
n
n
n条边。
题目中给出的
n
n
n点
n
n
n边就是一颗基环树。
题目先分为两类来讨论:
A
A
A在环上与
A
A
A不在环上。
题目分析
A在环上
如果 A A A在环上,那么它就可以一直与 B B B绕着环跑(除了 A A A和 B B B一来就在同一个点的情况,但是题目中约束了 x i ≠ y i x_i\not=y_i xi=yi),就一定是 A A A获胜。
A不在环上
这时候由于两者都是绝顶聪明的,那么对于需要逃生的
A
A
A来说,最好的情况一定是跑到环上,因为这样就可以到了必胜的条件1。
但是,在
A
A
A移动的同时,
B
B
B也会移动,比如存在以下情况。
(红色为
A
A
A,蓝色为
B
B
B)
当 A A A跑到环上的时候, B B B也会到达 A A A所在的点(黄色的点), B B B必胜。
思路
通过上面的分析,可以得出两个步骤:
1.找出图中的环(有且只有一个),判断
A
A
A是否在环上。
2.若
A
A
A不在环上,比较
A
A
A到环上的距离与
B
B
B到距离
A
A
A最近的环上节点的距离(就是上图中蓝色点到黄色点的距离)。
实现
环的判断
对于图中的环,因为只有一个,所以可以用拓扑来求出所有环上的点。
怎么求呢?
对于一个无向图,当一个点的度数为
1
1
1时,那么就只有一条边连接它,其肯定不是环上的点,在一棵基环树中,这种点就是叶子节点。
所以,依次删去叶子结点,然后产生新的叶子结点,再删,最后删不掉的,就是连接了两个点的环。
queue<int>o;
for(int i=1;i<=n;i++)if(d[i]==1)o.push(i),vis[i]=1;
while(!o.empty()){
int x=o.front();
o.pop();
for(int i=0;i<a[x].size();i++){
int p=a[x][i];
if(--d[p]==1)o.push(p),vis[p]=1;
}
}
环外点到环的距离
我们可以考虑依次将换上的点入队,向环外找不是环上的点,依次记录步数。
相当于把环当成一个点来看。
while(!q.empty()){
int x=q.front();
q.pop();
for(int i=0;i<a[x].size();i++){
int y=a[x][i];
if(vvs[y])continue;
vvs[y]=1;
dis[y]=dis[x]+1;//记录步数
q.push(y);
}
}
环上两点的距离
我们先随便在环上找一个点,将它标记为 1 1 1,然后用DFS遍历整个环,将剩下的每个点依次编号为 2 2 2, 3 3 3, 4 4 4……(不要用BFS,因为要么顺时针要么逆时针),对于环上两点 X X X, Y Y Y的最小距离,使用两个点的编号来计算
mn=min(abs(dds[X]-dds[Y]),cnt-abs(dds[X]-dds[Y]));//dds[i]是点i的编号,cnt是环的长度
距离 x x x最近的环上节点
对于上图中的红色点,要怎么求黄色的点。
在寻找环外点到环的距离时,可以记录一下每个点对应的环上的点,修改后如下:
for(int i=1;i<=n;i++)f[i]=i;
while(!q.empty()){
int x=q.front();
q.pop();
for(int i=0;i<a[x].size();i++){
int y=a[x][i];
if(vvs[y])continue;
vvs[y]=1;
dis[y]=dis[x]+1;
f[y]=f[x];
q.push(y);
}
}
然后……这道题就解决了。
代码:
#include<bits/stdc++.h>
#define endl putchar('\n')
using namespace std;
const int N=1e6+5;
int read(){
int x=0,f=1;
char c=getchar();
while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
while(c>='0'&&c<='9')x=(x<<3)+(x<<1)+c-'0',c=getchar();
return x*f;
}
void print(int x){
if(x<0)putchar('-'),x=-x;
if(x<10){putchar(x+'0');return;}
print(x/10);
putchar(x%10+'0');
}
void putstr(string s){
for(int i=0;i<s.size();i++)putchar(s[i]);
}
int n,m;
vector<int>a[N];
int vis[N];
int d[N];
int dis[N];
int vvs[N];
int dds[N];
int vvv[N];
int f[N];
int cnt;
void dfs(int x,int r){
dds[x]=r;
for(int i=0;i<a[x].size();i++){
int y=a[x][i];
if(vis[y])continue;
if(vvv[y])continue;
vvv[y]=1;
dfs(y,r+1);
vvv[y]=0;
break;
}
}
signed main(){
n=read(),m=read();
for(int i=1;i<=n;i++){
int u=read(),v=read();
a[u].push_back(v);
a[v].push_back(u);
d[v]++,d[u]++;
}
queue<int>o;
for(int i=1;i<=n;i++)if(d[i]==1)o.push(i),vis[i]=1;
while(!o.empty()){
int x=o.front();
o.pop();
for(int i=0;i<a[x].size();i++){
int p=a[x][i];
if(--d[p]==1)o.push(p),vis[p]=1;
}
}
queue<int>q;
for(int i=1;i<=n;i++){
f[i]=i;
dis[i]=1e9;
if(vis[i])continue;
dis[i]=0;
vvs[i]=1;
vvv[i]=0;
q.push(i);
}
while(!q.empty()){
int x=q.front();
q.pop();
for(int i=0;i<a[x].size();i++){
int y=a[x][i];
if(vvs[y])continue;
vvs[y]=1;
dis[y]=dis[x]+1;
f[y]=f[x];
q.push(y);
}
}
for(int i=1;i<=n;i++){
if(vis[i])continue;
vvv[i]=1;
dds[i]=1;
dfs(i,1);
break;
}
for(int i=1;i<=n;i++)cnt+=(!vis[i]);
while(m--){
int x=read(),y=read();
if(!vis[x])putstr("Survive");
else{
int X=f[x],Y=f[y];
int p=min(abs(dds[X]-dds[Y]),cnt-abs(dds[X]-dds[Y]));
if(dis[x]<dis[y])putstr("Survive");
else{
if(dis[x]<dis[y]+p)putstr("Survive");
else putstr("Deception");
}
}
endl;
}
}
后续:另一道关于基环树的题目
P8269 [USACO22OPEN] Visits S
这道题目为什么评绿啊?
其实很简单,经过模拟可以发现,不在环中的点,一定可以达到目标,而环中的点,总有一个点达不到目标,所以最后用所有的
v
i
v_i
vi之和减去环中的最小
v
i
v_i
vi就行了。
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=1e6+5;
int read(){
int x=0,f=1;
char c=getchar();
while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
while(c>='0'&&c<='9')x=(x<<3)+(x<<1)+c-'0',c=getchar();
return x*f;
}
void print(int x){
if(x<0)putchar('-'),x=-x;
if(x<10){putchar(x+'0');return;}
print(x/10);
putchar(x%10+'0');
}
int n;
int a[N],v[N];
int d[N];
int vis[N];
int ans;
int mn;
void dfs(int x){
vis[x]=1;
mn=min(mn,v[x]);
if(vis[a[x]])return;
dfs(a[x]);
return;
}
signed main(){
n=read();
for(int i=1;i<=n;i++)a[i]=read(),v[i]=read(),d[a[i]]++,ans+=v[i];
queue<int>q;
for(int i=1;i<=n;i++)if(d[i]==0)q.push(i);
while(!q.empty()){
int t=a[q.front()];
vis[q.front()]=1;
q.pop();
if(--d[t]==0)q.push(t);
}
for(int i=1;i<=n;i++){
if(vis[i])continue;
mn=1e9;
dfs(i);
ans-=mn;
}
print(ans);
}