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 }
    码量其实也不大
  • 相关阅读:
    SuperMap房产测绘成果管理平台
    SuperMap产权登记管理平台
    Android adb shell am 的用法(1)
    由浅入深谈Perl中的排序
    Android 内存监测和分析工具
    Android 网络通信
    adb server is out of date. killing...
    引导页使用ViewPager遇到OutofMemoryError的解决方案
    adb logcat 详解
    How to send mail by java mail in Android uiautomator testing?
  • 原文地址:https://www.cnblogs.com/hzoi-DeepinC/p/11328385.html
Copyright © 2011-2022 走看看