CF1381D The Majestic Brown Tree Snake

发布时间 2023-11-09 14:56:40作者: int_R

原题链接

膜拜 APJ 大神。

某人说这个题让他联想到“詹天佑”了。

考虑将图画成——给定链在最上方,不在给出链上的点都相当于挂在这条链上某个点上的树。

有两种情况:一种情况是进入一颗树,在其中完成调头,然后原路返回;还有一种情况是进入一颗树,然后出去的时候走向进来的反方向,然后再倒着回去。

第一种情况需要可以从一个方向进入,树中有一个点有至少两个的长度大于等于链长的子树(计算长度时算上这个点);第二种情况需要从两个方向都可以进入,树的最深深度大于等于链长(计算长度时算上树根)。

所以对于每颗树中你可以跑 dfs 得到信息。总和是 \(O(n)\) 的。

然后在链上,你能从右边/左边进入一棵树,一定是左端点可以移动到大于等于这个树根的位置/右端点可以移动到小于等于这个树根的位置。你考虑每次就相当于是当前链左边走进一棵树,再链右边走进一棵树,再链左边走进一棵树,再链右边走进一棵树……这样可以不断拓展可以到达的位置。

这个东西可以维护头尾两个指针,代表左链头可以到的最右端点,右链头可以到的最左端点。每棵挂在一个点上的树最多只需要从两个方向各进入一次,来更新一下这两个指针,时间复杂度 \(O(n)\) 的。

所有操作都是 \(O(n)\) 所以就是 \(O(n)\)。代码实现也没有很复杂。

#include<stdio.h>
#include<iostream>
#include<algorithm>
#include<vector>
using namespace std;
const int MAXN=1e6+10;
int T,n,s,t,a[MAXN],len,d[MAXN],cur;
bool b[MAXN],f1[MAXN],f2[MAXN];//f1,f2 分别表示链上的一个点是否符合第一,二种情况
vector <int> v[MAXN];
inline void clear()
{
    for(int i=1;i<=n;++i)
        b[i]=f1[i]=f2[i]=d[i]=0,v[i].clear();
    len=0;return ;
}
bool get_line(int x,int cnt=1,int fa=0)//得到这条链
{
    if(x==t){a[len=cnt]=x;return true;}
    bool flag=false;
    for(int y:v[x]) if(y!=fa) flag|=get_line(y,cnt+1,x);
    if(flag) a[cnt]=x;return flag;
}
void dfs(int x,int fa=0)//处理挂在链上的树
{
    int se=0;d[x]=1;
    for(int y:v[x])
    {
        if(y==fa||b[y]) continue;
        dfs(y,x);
        if(d[y]+1>d[x]) se=d[x],d[x]=d[y]+1;
        else if(d[y]+1>se) se=d[y]+1;
    }
    f1[cur]|=(se>=len);return ;
}
inline void work()
{
    cin>>n>>s>>t;
    for(int i=1,x,y;i<n;++i)
        cin>>x>>y,v[x].push_back(y),v[y].push_back(x);
    get_line(s);for(int i=1;i<=len;++i) b[a[i]]=true;
    for(int x=1;x<=len;++x) cur=x,dfs(a[x],0),f2[x]=(d[a[x]]>=len);
    int l=1,r=len,prel=0,prer=len+1;
    while(l!=prel||r!=prer)//这里不断拓展两个指针
    {
        for(int i=prel+1;i<=l;++i) r=min(r,max(i+1,i+(len-d[a[i]])));prel=l;
        for(int i=prer-1;i>=r;--i) l=max(l,min(i-1,i-(len-d[a[i]])));prer=r;
    }
    for(int x=1;x<=len;++x)
    {
        if((l>=x||r<=x)&&f1[x]){cout<<"YES\n";return ;}
        if((l>=x&&r<=x)&&f2[x]){cout<<"YES\n";return ;}
    }
    cout<<"NO\n";return ;
}
int main()
{
    freopen("chtholly.in","r",stdin);
    freopen("chtholly.out","w",stdout);
    cin.tie(0),cout.tie(0);
    ios::sync_with_stdio(0);
    cin>>T;
    while(T--) work(),clear();
    return 0;
}