zoukankan      html  css  js  c++  java
  • 【tarjan 拓扑排序 dp】bzoj1093: [ZJOI2007]最大半连通子图

    思维难度不大,关键考代码实现能力。一些细节还是很妙的。

    Description

      一个有向图G=(V,E)称为半连通的(Semi-Connected),如果满足:?u,v∈V,满足u→v或v→u,即对于图中任意
    两点u,v,存在一条u到v的有向路径或者从v到u的有向路径。若G'=(V',E')满足V'?V,E'是E中所有跟V'有关的边,
    则称G'是G的一个导出子图。若G'是G的导出子图,且G'半连通,则称G'为G的半连通子图。若G'是G所有半连通子图
    中包含节点数最多的,则称G'是G的最大半连通子图。给定一个有向图G,请求出G的最大半连通子图拥有的节点数K
    ,以及不同的最大半连通子图的数目C。由于C可能比较大,仅要求输出C对X的余数。

    Input

      第一行包含两个整数N,M,X。N,M分别表示图G的点数与边数,X的意义如上文所述接下来M行,每行两个正整
    数a, b,表示一条有向边(a, b)。图中的每个点将编号为1,2,3…N,保证输入中同一个(a,b)不会出现两次。N ≤1
    00000, M ≤1000000;对于100%的数据, X ≤10^8

    Output

      应包含两行,第一行包含一个整数K。第二行包含整数C Mod X.

    Sample Input

    6 6 20070603
    1 2
    2 1
    1 3
    2 4
    5 6
    6 4

    Sample Output

    3
    3

    题目分析

    首先来分析一下题目给的约束条件到底在描述一个什么东西。

    半连通子图?

    乍一看好像“半连通子图”是个非常麻烦的东西,但是显然一个环肯定是一个半连通子图,于是我们可以先缩点。

    缩完点之后就变成一个DAG了,这时多画几个图就会发现,若一个子图是半连通子图,则它必定是一条链。这个其实是挺显然的,这里就不用形式化的语言描述了。

    于是求最大半连通子图就变成了求:缩点后,求有向图带点权的最长链。

    方案数

    那么DAG求最长链很容易,记忆化搜索或者拓扑排序都可以。求方案数的话,也就是类似dp的方法,用$g[i]$表示以$i$为终点最长链的方案数,转移起来也挺方便的。

    对了为了统计方案数,连通块之间的连边需要去重。

    从黄学长博客上学到一种挺巧妙的去重方法(虽然说知道后很简单,但是还是很妙的),就是对于$(u,v)$,每次操作完记录$vis[v]=u$,若遇到$vis[v]=u$则退出。

    于是就变成了:tarjan+拓扑+dp的板子汇总题

    我是把之前写的tarjan板子套上去了,所以有点长……都3k了

      1 #include<bits/stdc++.h>
      2 typedef long long ll;
      3 const int maxn = 100035;
      4 const int maxm = 1200035;
      5 
      6 int n,m;
      7 int deg[maxn],vis[maxn],q[maxn],qHead,qTail;
      8 int head[maxn],nxt[maxm],edges[maxm],edgeTot;
      9 ll p,col[maxn],cols,size[maxn],f[maxn],g[maxn];
     10 ll mx,cnt;
     11 
     12 int read()
     13 {
     14     char ch = getchar();
     15     int num = 0;
     16     bool fl = 0;
     17     for (; !isdigit(ch); ch = getchar())
     18         if (ch=='-') fl = 1;
     19     for (; isdigit(ch); ch = getchar())
     20         num = (num<<1)+(num<<3)+ch-48;
     21     if (fl) num = -num;
     22     return num;
     23 }
     24 namespace tarjanSpace
     25 {
     26     int stk[maxn],cnt;
     27     int a[maxn],dfn[maxn],low[maxn],tim;
     28     int edgeTot,edges[maxm],nxt[maxm],head[maxn];
     29     void tarjan(int now)
     30     {
     31         dfn[now] = low[now] = ++tim, stk[++cnt] = now;
     32         for (int i=head[now]; i!=-1; i=nxt[i])
     33         {
     34             int v = edges[i];
     35             if (!dfn[v])
     36                 tarjan(v), low[now] = std::min(low[now], low[v]);
     37             else if (!col[v])
     38                 low[now] = std::min(low[now], dfn[v]);
     39         }
     40         if (low[now]==dfn[now])
     41         {
     42             ::col[now] = ++::cols, ::size[cols] = 1;
     43             for (; stk[cnt]!=now; cnt--, ::size[cols]++)
     44                 ::col[stk[cnt]] = ::cols;
     45             cnt--;
     46         }
     47     }
     48     inline void addedgeInner(int u, int v)
     49     {
     50         edges[++edgeTot] = v, nxt[edgeTot] = head[u], head[u] = edgeTot;
     51     }
     52     inline void addedgeOuter(int u, int v)
     53     {
     54         deg[v]++, ::edges[++::edgeTot] = v, ::nxt[::edgeTot] = ::head[u], ::head[u] = ::edgeTot;
     55     }
     56     void dealOuter()
     57     {
     58         for (int i=1; i<=n; i++)
     59         {
     60             int u = col[i];
     61             for (int j=head[i]; j!=-1; j=nxt[j])
     62                 if (u!=col[edges[j]])
     63                     addedgeOuter(u, col[edges[j]]);
     64         }
     65         cols--;
     66     }
     67     void solve()
     68     {
     69         memset(head, -1, sizeof head);
     70         cnt = tim = edgeTot = 0;
     71         for (int i=1; i<=n; i++) addedgeInner(0, i);
     72         for (int i=1; i<=m; i++)
     73         {
     74             int u = read(), v = read();
     75             addedgeInner(u, v);
     76         }
     77         tarjan(0);
     78         dealOuter();
     79     }
     80 }
     81 void topoSort()
     82 {
     83     qHead = 0, qTail = 0;
     84     for (int i=1; i<=cols; i++)
     85     {
     86         if (!deg[i]) q[++qTail] = i;
     87         f[i] = size[i], g[i] = 1;
     88     }
     89     while (qHead!=qTail)
     90     {
     91         int u = q[++qHead];
     92         for (int i=head[u]; i!=-1; i=nxt[i])
     93         {
     94             int v = edges[i];
     95             if ((--deg[v])==0) q[++qTail] = v;
     96             if (vis[v]==u) continue;
     97             if (f[v] < f[u]+size[v])
     98                 f[v] = f[u]+size[v], g[v] = g[u];
     99             else if (f[v]==f[u]+size[v])
    100                 g[v] = (g[v]+g[u])%p;
    101             vis[v] = u;
    102         }
    103     }
    104 }
    105 int main()
    106 {
    107     memset(head, -1, sizeof head);
    108     n = read(), m = read(), p = read();
    109     tarjanSpace::solve();
    110     topoSort();
    111     for (int i=1; i<=cols; i++)
    112         if (f[i] > mx)
    113             mx = f[i], cnt = g[i];
    114         else if (f[i]==mx)
    115             cnt = (cnt+g[i])%p;
    116     printf("%lld
    %lld
    ",mx,cnt);
    117     return 0;
    118 }

    END

  • 相关阅读:
    HDU 5273 Dylans loves sequence 暴力递推
    HDU 5285 wyh2000 and pupil 判二分图+贪心
    HDU 5281 Senior's Gun 贪心
    HDU 5651 xiaoxin juju needs help 逆元
    HDU 5646 DZY Loves Partition
    HDU 5366 The mook jong
    HDU 5391Z ball in Tina Town 数论
    HDU 5418 Victor and World 允许多次经过的TSP
    HDU 5642 King's Order dp
    抽屉原理
  • 原文地址:https://www.cnblogs.com/antiquality/p/9265364.html
Copyright © 2011-2022 走看看