zoukankan      html  css  js  c++  java
  • [USACO08DEC]Trick or Treat on the Farm

    嘟嘟嘟

    这道题有一个特别重要的一点,就是节点数为 n 的图只有 n 条边,于是就有一下几个性质:

    1.每一个点的出度都为1。

    2.一个k个节点的强连通分量都是有k条边的环,而且这个环不会通往其他的点,只可能有别的点通往这个环。

    所以说,对于一个在环中的点,答案就是这个环的节点数(包括自环),对于一个不在环中的点,答案就是通往环的距离+环的节点数。

    因此,首先用tarjan缩点,然后反向建立缩点后的图。为什么要反向建呢?先考虑对于上文第二种情况,暴力的做法就是每一次遇到这样的一个点,就暴力的一步一步找,直到找到环为止。然而如果整张图只有一个自环,时间复杂度就会退化到O(n2)。因此要想办法维护每一个环外的点到环的距离。如果我们反向建边,就相当于求每一个环到环外点的距离,而且因为环外点之间都只有一条边,因此路径是唯一的,所以只要bfs一次就能求出通往这个环的点的距离。

    还有几点要注意:

    1.就是上文求出的距离的节点编号都是缩点后的编号,和原图编号不一样,因此我们要建立一个从新图编号到原图编号的映射。又因为这个映射是一对多,所以还得开一个vector存。这个在tarjan的时候就可以顺便处理出来。

    2.缩点后,自环和单点是没有区别的,所以必须先把是自环的点标记一下。代码中又开了一个vector,把是自环的点都存了下来(不知有没有更好的方法)。

      1 #include<cstdio>
      2 #include<iostream>
      3 #include<algorithm>
      4 #include<cmath>
      5 #include<cstring>
      6 #include<cstdlib>
      7 #include<vector>
      8 #include<queue>
      9 #include<stack>
     10 #include<cctype>
     11 using namespace std;
     12 #define enter puts("")
     13 #define space putchar(' ')
     14 #define Mem(a) memset(a, 0, sizeof(a))
     15 typedef long long ll;
     16 typedef double db;
     17 const int INF = 0x3f3f3f3f;
     18 const db eps = 1e-8;
     19 const int maxn = 1e5 + 5;
     20 inline ll read()
     21 {
     22     ll ans = 0;
     23     char ch = getchar(), last = ' ';
     24     while(!isdigit(ch)) last = ch, ch = getchar();
     25     while(isdigit(ch)) ans = (ans << 3) + (ans << 1) + ch - '0', ch = getchar();
     26     if(last == '-') ans = -ans;
     27     return ans;
     28 }
     29 inline void write(ll x)
     30 { 
     31     if(x < 0) putchar('-'), x = -x;
     32     if(x >= 10) write(x / 10);
     33     putchar(x % 10 + '0');
     34 }
     35 
     36 int n;
     37 vector<int> v[maxn];
     38 
     39 int dfn[maxn], low[maxn], cnt = 0;
     40 bool in[maxn];
     41 stack<int> st;
     42 int col[maxn], val[maxn], ccol = 0;
     43 vector<int> pos[maxn];            //映射 
     44 void tarjan(int now)
     45 {
     46     dfn[now] = low[now] = ++cnt;
     47     st.push(now); in[now] = 1;
     48     for(int i = 0; i < (int)v[now].size(); ++i)
     49     {
     50         if(!dfn[v[now][i]])
     51         {
     52             tarjan(v[now][i]);
     53             low[now] = min(low[now], low[v[now][i]]);
     54         }
     55         else if(in[v[now][i]]) low[now] = min(low[now], dfn[v[now][i]]);
     56     }
     57     if(low[now] == dfn[now])
     58     {
     59         int x; ccol++;
     60         do
     61         {
     62             x = st.top();
     63             in[x] = 0; st.pop();
     64             col[x] = ccol;
     65             pos[ccol].push_back(x);
     66             val[ccol]++;
     67         }while(x != now);
     68     }
     69     return;
     70 }
     71 
     72 vector<int> v2[maxn];            //新图 
     73 void newGraph(int now)
     74 {
     75     for(int i = 0; i < (int)v[now].size(); ++i)
     76     {
     77         int x = col[now], y = col[v[now][i]];
     78         if(x == y) continue;
     79         v2[y].push_back(x);            //反向建边 
     80     }
     81 }
     82 
     83 vector<int> ccir;                    //储存是自环的点 
     84 int ans[maxn], ans2[maxn];            //ans[]是新图的节点编号的答案,ans2[]是原图的 
     85 
     86 void bfs(int s)
     87 {
     88     ans[s] = val[s];
     89     queue<int> q;
     90     q.push(s);
     91     while(!q.empty())
     92     {
     93         int now = q.front(); q.pop();
     94         for(int i = 0; i < v2[now].size(); ++i)
     95         {
     96             ans[v2[now][i]] = ans[now] + 1;
     97             q.push(v2[now][i]);
     98         }
     99     }
    100 }
    101 
    102 int main()
    103 {
    104     n = read();
    105     for(int i = 1; i <= n; ++i) 
    106     {
    107         int x = read();
    108         v[i].push_back(x);
    109         if(i == x) ccir.push_back(i);        //该点是自环 
    110     }
    111     for(int i = 1; i <= n; ++i) if(!dfn[i]) tarjan(i);
    112     for(int i = 1; i <= n; ++i) newGraph(i);
    113     for(int i = 0; i < (int)ccir.size(); ++i) ans[col[ccir[i]]] = -1;        //把是自环的点在新图上标记一下 
    114     for(int i = 1; i <= ccol; ++i) if(val[i] > 1 || ans[i] == -1) bfs(i);    
    115     //如果这个点是一个环(包括自环),就维护所有能到达他的点的距离 
    116     for(int i = 1; i <= ccol; ++i) 
    117         for(int j = 0; j < (int)pos[i].size(); ++j) ans2[pos[i][j]] = ans[i];    //再将答案映射到原图上 
    118     for(int i = 1; i <= n; ++i) write(ans2[i]), enter;
    119     return 0;
    120 }
    View Code
  • 相关阅读:
    spring+hibernate常见异常集合
    Java报错原因汇总
    java常见异常集锦
    连接池 druid(阿里巴巴的框架)
    企业支付宝账号开发接口实现
    Maven使用常见问题整理
    MySQL的分页
    Struts2中通配符的使用
    Centos下安装mysql 总结
    将linux用在开发环境中
  • 原文地址:https://www.cnblogs.com/mrclr/p/9534749.html
Copyright © 2011-2022 走看看