zoukankan      html  css  js  c++  java
  • JOISC2014 Day2 E "交朋友" (思维+假的SCC)

    传送门

    题目描述
    你是活跃在历史幕后的一名特工,为了世界和平而夜以继日地努力着。
    这个世界有N个国家,编号为1..N;
    你的目的是在这N个国家之间建立尽可能多的友好关系。
    你为了制定一个特工工作的计划,作出了一张当今国际关系的示意图。
    你准备了一张非常大的画纸,先画下了代表每个国家的N个点。
    接下来,为了表示现在的国际关系,画下了M个连接两个国家的有向边;
    其中从国家u连向国家v的有向边,表示国家u向国家v派遣了大使,下文称作边(u,v)。
    这样就做出了N个点M条边的当今国际关系示意图。
    
    作为两国友好关系的开端,两国之间需要进行「友好条约缔结会议」,以下简称会议。
    如果某两个国家p和q要进行会议,那么需要一个向两国都派遣了大使的国家x作为中介。
    会议结束后,会议的双方相互向对方的国家派遣大使。
    换句话说,为了让国家p和国家q进行会议,必须存在一个国家x满足边(x,p)和边(x,q)都存在;
    并且在会议后添加两条边(p,q)和(q,p)(如果需要添加的某条边已经存在则不添加)。
    你的工作是对于可以进行会议的两国,选择会议的中介并促使会议进行。
    使用这张图进行工作的模拟的话,世界距离和平还有多远的一个重要的基准就是这张图上的边数。
    
    现在给出国家的个数以及当今国际关系的情报,请你求出反复选择两个国家,促使它们进行会议后,图上最多会有多少条边。
    
    输入
    第一行两个空格分隔的整数N和M,分别表示世界上国家的个数和图中的边数。
    接下来M行描述画纸上的有向边的信息,其中第i行有两个空格分隔的整数ai和bi,表示图中有一条从ai到bi的有向边。
    
    输出
    输出一行一个整数,表示能实现的边数的最大值。
    注意这个边数包括原有的边数和新连接的边数。
    题目描述
    样例输入
    5 4
    1 2
    1 3
    4 3
    4 5
    
    样例输出
    10
    
    样例解释
    国家1作为中介国,国家2与3开会。
    国家4作为中介国,国家3与5开会。
    国家3作为中介国,国家2与5开会。
    样例输入输出

    数据范围:1≤N≤105,1≤M≤2×105,1≤ai,bi≤N,ai≠bi,(ai,bi)≠(aj,bj)

    参考资料:

      [1]:http://icpc.upc.edu.cn/blog/?p=108

    题意(摘抄自[1]):

      如果存在有向边<a,b>和<a,c>,就添加两条有向边<b,c>和<c,b>(已经存在就不添加),问最多有多少条边;

    题解:

      昨天比赛做这道题的时候,想到了SCC,但我真的用SCC了,中间过程处理的不好(代码写搓了,逃)........

      今天晚上补这道题,看了看题解,和昨天自己想的差不多,就是代码实现上,[1]并没有用SCC知识用SCC思考这道题的做法;

      哎,欠缺的还是太多了,理解了将近半个小时,终于理解了,tql;

      下面谈谈我的进一步理解:

      (看会专业课先,理解明天写,下周要考两门,orz)

      对于某节点 u,如果 u 有 > 1 个儿子 v1,v,.......,vx,那么 v1,v,.......,vx  及其所有儿子可构成强连通;

      例如:

      

      节点①有两个儿子②③,那么②及其所有儿子④⑥,③及其所有儿子⑤构成强连通;

      即②③④⑤⑥构成强连通;

      同属于同一个强连通的所有节点的信息可以集中到一个节点上,假设这5个节点的信息集中到节点②上;

    1 int fa[maxn];///fa[i]:i所处的强连通的代表节点
    2 ll tot[maxn];///tot[i]:i所处的强连通的个数,只有代表节点的tot[i]才有意义

      fa初始化为-1,tot初始化为1;

      对于上图,合并后的信息为:

      fa[2]=-1,fa[3,4,5,6]=2;

      tot[2]=5;(只有②节点的tot有用)

      那么,最重要的就是合并操作,具体如下:

     1 int Find(int x)///查找x所处的强连通的代表节点
     2 {
     3     return fa[x] == -1 ? x:fa[x]=Find(fa[x]);
     4 }
     5 void DFS(int u,int x)
     6 {
     7     for(int i=head[u];~i;i=G[i].next)
     8     {
     9         int y=G[i].to;
    10 
    11         y=Find(y);
    12         if(x == y)
    13             continue;
    14 
    15         fa[y]=x;
    16         tot[x] += tot[y];
    17         DFS(y,x);
    18     }
    19 }
    20 
    21 ///合并
    22 bool update=true;
    23 while(update)
    24 {
    25     update=false;
    26     for(int u=1;u <= n;++u)
    27     {
    28         ///判断u是否已经处于某个强连通中
    29         ///如果u处于某个强连通中,x=u,反之x=-1
    30         int x=tot[Find(u)] == 1 ? -1:u;
    31         for(int i=head[u];~i;i=G[i].next)
    32         {
    33             /**
    34                 如果u不处于某个强连通中:
    35                     ①如果其包含>1个儿子节点,其儿子节点可以构成强连通
    36                 如果u本身就处于某个强连通中:
    37                     ①其所有儿子全部在这个强连通中
    38             */
    39             int y=G[i].to;
    40             if(x == -1)
    41                 x=y;
    42             else///将强连通中的节点信息合并到Find(x)中
    43             {
    44                 x=Find(x);
    45                 y=Find(y);
    46 
    47                 if(x == y)///在同一个强连通中
    48                     continue;
    49 
    50                 update=true;
    51                 fa[y]=x;
    52                 tot[x] += tot[y];
    53                 DFS(y,x);///将y的所有儿子节点合并到Find(x)中
    54             }
    55         }
    56     }
    57 }

    AC代码(偷偷换成了我的风格):

      1 #include<bits/stdc++.h>
      2 using namespace std;
      3 #define ll long long
      4 #define memF(a,b,n) for(int i=0;i <= n;a[i]=b,++i);
      5 const int maxn=1e5+50;
      6 
      7 int n,m;
      8 int num;
      9 int head[maxn];
     10 struct Edge
     11 {
     12     int to;
     13     int next;
     14 }G[maxn<<1];
     15 void addEdge(int u,int v)
     16 {
     17     G[num]={v,head[u]};
     18     head[u]=num++;
     19 }
     20 int fa[maxn];///fa[i]:i所处的强连通的代表节点
     21 ll tot[maxn];///tot[i]:i所处的强连通的个数,只有代表节点的tot[i]才有意义
     22 int Find(int x)///查找x所处的强连通的代表节点
     23 {
     24     return fa[x] == -1 ? x:fa[x]=Find(fa[x]);
     25 }
     26 void DFS(int u,int x)
     27 {
     28     for(int i=head[u];~i;i=G[i].next)
     29     {
     30         int y=G[i].to;
     31 
     32         y=Find(y);
     33         if(x == y)
     34             continue;
     35 
     36         fa[y]=x;
     37         tot[x] += tot[y];
     38         DFS(y,x);
     39     }
     40 }
     41 ll Solve()
     42 {
     43     memF(fa,-1,n);
     44     memF(tot,1,n);
     45     bool update=true;
     46     while(update)
     47     {
     48         update=false;
     49         for(int u=1;u <= n;++u)
     50         {
     51             ///判断u是否已经处于某个强连通中
     52             ///如果u处于某个强连通中,x=u,反之x=-1
     53             int x=tot[Find(u)] == 1 ? -1:u;
     54             for(int i=head[u];~i;i=G[i].next)
     55             {
     56                 /**
     57                     如果u不处于某个强连通中:
     58                         ①如果其包含>1个儿子节点,其儿子节点可以构成强连通
     59                     如果u本身就处于某个强连通中:
     60                         ①其所有儿子全部在这个强连通中
     61                 */
     62                 int y=G[i].to;
     63                 if(x == -1)
     64                     x=y;
     65                 else///将强连通中的节点信息合并到Find(x)中
     66                 {
     67                     x=Find(x);
     68                     y=Find(y);
     69 
     70                     if(x == y)///在同一个强连通中
     71                         continue;
     72 
     73                     update=true;
     74                     fa[y]=x;
     75                     tot[x] += tot[y];
     76                     DFS(y,x);///将y的所有儿子节点合并到Find(x)中
     77                 }   
     78             }
     79         }
     80     }
     81 
     82     ll ans=0;
     83     for(int u=1;u <= n;++u)
     84     {
     85         if(fa[u] == -1)
     86             ans += tot[u]*(tot[u]-1);
     87         for(int i=head[u];~i;i=G[i].next)
     88         {
     89             int x=Find(u);
     90             int y=Find(G[i].to);
     91             if(x != y)
     92                 ans++;
     93         }
     94     }
     95 
     96     return ans;
     97 }
     98 void Init()
     99 {
    100     num=0;
    101     memF(head,-1,n);
    102 }
    103 int main()
    104 {
    105     scanf("%d%d",&n,&m);
    106     Init();
    107     for(int i=1;i <= m;++i)
    108     {
    109         int u,v;
    110         scanf("%d%d",&u,&v);
    111         addEdge(u,v);
    112     }
    113     printf("%lld
    ",Solve());
    114 
    115     return 0;
    116 }
    View Code
  • 相关阅读:
    浅谈流形学习
    流形(Manifold)初步【转】
    MATLAB中的函数的归总
    LBP特征提取实现
    Nginx 安装
    Linux执行.sh文件,提示No such file or directory的问题
    Ubuntu 安装后的配置及美化(二)
    Ubuntu 安装后的配置及美化(一)
    关于windows上 web 和 ftp 站点的创建及使用
    win10 + Lubuntu 双系统安装
  • 原文地址:https://www.cnblogs.com/violet-acmer/p/10946473.html
Copyright © 2011-2022 走看看