• [cf1361E]James and the Chase


    称一个点是"好点",当且仅当其到其余所有点恰存在一条简单路径

    结论1:$x$为好点当且仅当以$x$为根的dfs树包含所有点且非树边均为返祖边

    若不包含所有点,那么$x$到不被包含的点即不存在简单路径

    若存在非树边不为返祖边,则不论如何该边$x$到该边终点存在至少两条简单路径

    另一方面,考虑在这样一棵dfs树中的简单路径,将其用非树边划分为若干段(段的内部只能用树边),每一段树边构成一条链,则显然有以下性质:

    1.链无公共点

    2.第一条链链顶为起点,最后一条链链尾为终点

    3.每一条链链尾存在到下一条链链顶的(返祖)边

    4.每一条链链顶是上一条链链顶的祖先(其实是1和3的推论)

    由此,考虑从$x$到$y$的简单路径,最后一条链链顶是$x$的祖先(4的推论),而该链链尾为$y$,因此$x$到$y$的简单路径数必然经过$lca(x,y)$

    特别的,若$x$是$y$的祖先,那么为了不重复经过$x$,必然仅有一条链且恰为$x$到$y$

    更特别的,当$x$是根时其是任意一点的祖先,因此到任意一点恰存在一条简单路径,即得证

    根据此结论,即可$o(n)$判定一个好点

    进一步的,不断随机一个点$x$并判断其是否是好点,若随机$T$次后仍找不到好点,那么好点数严格小于20%的概率即有$1-frac{1}{5^{T}}$,当$T=100$时可以看作1,也即不需要输出

    由此即可得到一个好点,将其记作$rt$,并以$rt$为根建立dfs树(以下均指此树)

    称一条非树边"通过"$x$当且仅当其以$x$子树内(包括$x$)为起点且到达$x$的祖先

    结论2:$x$为好点当且仅当$x=rt$或恰存在一条非树边通过$x$且该非树边的终点为好点

    $x=rt$的情况显然,不妨假设$x e rt$

    此时,如果不存在非树边通过$x$显然$x$无法到达$x$的祖先,如果存在多条非树边通过$x$则显然$x$到这两条非树边终点中较深的点存在至少两条简单路径,因此$x$均不为好点

    而若该非树边的终点不为好点,对其分类讨论:

    1.若其到某点不存在简单路径,注意到其可以到达$x$,那么$x$到该点一定也不存在简单路径

    2.若其到某点存在至少两条简单路径,该点显然不在其子树中(结合结论1的证明),那么其一定不会再经过$x$(到达$x$子树内后要离开必须重复经过自己),也即$x$到其的这一段不会使得简单路径重复经过某点,那么$x$到该点也存在至少两条简单路径

    另一方面,若该非树边的终点是好点,则对其余点分类讨论:

    1.$x$到$x$子树内的点,同样根据结论1的证明恰存在一条简单路径

    2.$x$到$x$子树外的点,必然要经过该好点且之后不会在经过$x$,在其简单路径的基础上补一段即可

    由此,简单递归即可求出所有好点

    时间复杂度为$o(Tn)$,可以通过

     1 #include<bits/stdc++.h>
     2 using namespace std;
     3 #define N 100005
     4 #define T 100
     5 vector<int>ans,v[N];
     6 int t,n,m,rt,flag,x,y,dfn[N],cnt[N],pos[N],vis[N];
     7 void dfs(int k){
     8     dfn[k]=++dfn[0],vis[k]=1;
     9     for(int i=0;i<v[k].size();i++)
    10         if (!dfn[v[k][i]]){
    11             dfs(v[k][i]);
    12             if ((!pos[k])||(dfn[pos[v[k][i]]]<dfn[pos[k]]))pos[k]=pos[v[k][i]];
    13             cnt[k]+=cnt[v[k][i]];
    14         }
    15         else{
    16             if (!vis[v[k][i]])flag=1;
    17             else{
    18                 if ((!pos[k])||(dfn[v[k][i]]<dfn[pos[k]]))pos[k]=v[k][i]; 
    19                 cnt[k]++,cnt[v[k][i]]--;
    20             }
    21         }
    22     vis[k]=0;
    23 }
    24 void check(int k){
    25     vis[k]=0;
    26     if ((k==rt)||(cnt[k]==1)&&(vis[pos[k]]))vis[k]=1;
    27     for(int i=0;i<v[k].size();i++)
    28         if (dfn[k]<dfn[v[k][i]])check(v[k][i]); 
    29 }
    30 int main(){
    31     srand(time(0));
    32     scanf("%d",&t);
    33     while (t--){
    34         scanf("%d%d",&n,&m);
    35         ans.clear();
    36         for(int i=1;i<=n;i++)v[i].clear();
    37         for(int i=1;i<=m;i++){
    38             scanf("%d%d",&x,&y);
    39             v[x].push_back(y);
    40         }
    41         for(int k=0;k<T;k++){
    42             rt=flag=dfn[0]=0;
    43             for(int i=0;i<20;i++)rt=(rt<<1)+rand()%2;
    44             rt=(rt+n-1)%n+1;
    45             for(int i=1;i<=n;i++)dfn[i]=cnt[i]=pos[i]=vis[i]=0;
    46             dfs(rt);
    47             if ((dfn[0]!=n)||(flag))continue;
    48             for(int i=1;i<=n;i++)vis[i]=0;
    49             check(rt);
    50             for(int i=1;i<=n;i++)
    51                 if (vis[i])ans.push_back(i);
    52             break;
    53         }
    54         if (5*ans.size()<n)printf("-1
    ");
    55         else{
    56             printf("%d",ans[0]);
    57             for(int i=1;i<ans.size();i++)printf(" %d",ans[i]);
    58             printf("
    ");
    59         }
    60     }
    61     return 0;
    62 }
    View Code
  • 相关阅读:
    关于Web 打印的三种方法
    禁止另存为 禁止复制 禁止右键 JS
    无限数据递归
    VS2003+Ajax Div文本框输入提示
    C#关于日期月天数和一年有多少周及某年某周时间段的计算
    git代码冲突
    RabbitMQ死信队列
    Elementui:ElContainer布局
    mxml调用as
    用自己的MapServer,解决沙箱冲突
  • 原文地址:https://www.cnblogs.com/PYWBKTDA/p/15408253.html
Copyright © 2020-2023  润新知