zoukankan      html  css  js  c++  java
  • 轰炸行动(bomb):tarjan,拓扑排序

    考场上看错题,没什么好说的。

    然而它就是一个大板子。

    发的题解勉强还能看。但是我还想再讲讲。

    题目的表述是,如果从A能直接或间接到B,那么就不能同时轰炸A和B。

    那么我们从图里随便拽出一条有向路径,从这条路径中随意挑2个点AB,那么要么能从A到B要么从B到A

    那么你任意挑出的这两个点只要不是同一个点那么就不能同时轰炸。

    带下划线的那一段有什么用呢?它的正确性是显然的。

    我所说的“有向路径”没有加任何附加限制,所以可以包括环,环上转几圈就可能出现同一个点呗。

    我们考虑单纯的一个环。那么它上的每一个都要单独炸一次。

    再考虑单纯的一条路径,那么路径上的每一个点也需要单独炸一次。

    如果一个路径进入了一个环,那么这上面的点也必须单独炸一次(路径上的点可以到达环上的任意点)。

    如果一个环引出了一个路径,那么环上的点亦可到路径上,都要单独炸一次。

    综上,就是要找出一条路径使它上面的不同的点尽量多,不同的点的个数即为答案。

    上面那一堆话里已经包括了这个意思:环上的每个点都会给路径长度增加1,且对联通性没有影响。

    所以考虑tarjan缩完强联通分量后就没有环了,只不过环变成了权值等于环中点数的一个大点而已

    其余普通点的权值为1。现在的问题就变成了在一个DAG里找一条路径使它上面的点权和最大。

    不能dfs,因为这是有向的DAG,虽然不是环但也不是树,它可以长得很恶心。

    在这个恶心图里面跑dfs就会多次重复地经过3和3下面的点导致大量浪费。

    可以倒着搜加个记忆化什么的,然而一个拓扑排序会更方便一些。

     1 #include<bits/stdc++.h>
     2 using namespace std;
     3 map<int,int>mm;
     4 int n,m,fir[1000005],l[1000005],to[1000005],cnt=1,dfn[1000005],low[1000005];
     5 int _fir[1000005],_l[1000005],_to[1000005],_cnt,w[1000005],in[1000005];
     6 int sta[1000005],ins[1000005],tim,top,bl[1000005],cnt_scc,ans,dis[1000005];
     7 int q[1000005],t;
     8 void connect(int a,int b){l[++cnt]=fir[a];fir[a]=cnt;to[cnt]=b;}
     9 void _connect(int a,int b){_l[++_cnt]=_fir[a];_fir[a]=_cnt;_to[_cnt]=b;in[b]++;}
    10 void tarjan(int p){
    11     dfn[p]=low[p]=++tim;sta[++top]=p;ins[p]=1;
    12     for(int i=fir[p];i;i=l[i])
    13         if(!dfn[to[i]])tarjan(to[i]),low[p]=min(low[p],low[to[i]]);
    14         else if(ins[to[i]])low[p]=min(low[p],low[to[i]]);
    15     if(dfn[p]==low[p]){
    16         w[++cnt_scc]++;
    17         while(sta[top]!=p)ins[sta[top]]=0,bl[sta[top--]]=cnt_scc,w[cnt_scc]++;
    18         top--;bl[p]=cnt_scc;ins[p]=0;
    19     }
    20 }
    21 signed main(){
    22     scanf("%d%d",&n,&m);
    23     for(int i=1,a,b;i<=m;++i)scanf("%d%d",&a,&b),connect(a,b);
    24     for(int i=1;i<=n;++i)if(!dfn[i])tarjan(i);
    25     for(int i=1;i<=n;++i)for(int j=fir[i];j;j=l[j])
    26         if(bl[i]!=bl[to[j]])_connect(bl[i],bl[to[j]]);
    27     for(int i=1;i<=cnt_scc;++i)if(!in[i])q[++t]=i;
    28     for(int h=1;h<=t;++h){
    29         int dt=dis[q[h]]+w[q[h]];ans=max(ans,dt);
    30         for(int i=_fir[q[h]];i;i=_l[i]){
    31             in[_to[i]]--;dis[_to[i]]=max(dis[_to[i]],dt);
    32             if(!in[_to[i]])q[++t]=_to[i];
    33         }
    34     }
    35     printf("%d
    ",ans);
    36 }
    码量其实也不大
  • 相关阅读:
    s3c2440——按键中断
    s3c2440——swi异常
    S3C2440的七种模式之——未定义模式(去掉bl print1 bug解决)
    Entity Framework DBContext 增删改查深度解析
    Git换行符是如何精确控制的
    Entity Framework 乐观并发处理
    Session, Token and SSO 有什么区别
    2017让我受益最大的那些书--别找了,都在这里
    GDB scheduler-locking 命令详解
    Java web servers 间是如何实现 session 同步的
  • 原文地址:https://www.cnblogs.com/hzoi-DeepinC/p/11328385.html
Copyright © 2011-2022 走看看