倍增lca-ZJOI2012 灾难

一个食物网有N个点,代表N种生物,如果生物x可以吃生物y,那么从y向x连一个有向边。这个图没有环。图中有一些点没有连出边,这些点代表的生物都是生产者,可以通过光合作用来生存; 而有连出边的点代表的都是消费者,它们必须通过吃其他生物来生存。如果某个消费者的所有食物都灭绝了,它会跟着灭绝。我们定义一个生物在食物网中的“灾难值”为,如果它突然灭绝,那么会跟着一起灭绝的生物的种数。现在请你求出每一个物种的“灾难值”。

  首先思考:什么情况下一个物种会灭绝?也就是所有它能吃的东西灭绝了,它就灭绝了。所以如果要让物种A灭绝,我们可以选一个物种B,使得物种A所有能吃的东西都灭绝。我们定义”灭绝树”,其中一个节点的父亲是能让它灭绝的深度最低的结点。不难发现灭绝树上A的祖先灭绝后,A一定灭绝。所以若要让物种x,y一起灭绝,只需查询它们所有父亲在灭绝树上的lca即可。

  这道题做了三天。。最后用cyaron对拍出来,发现是一个以前写的模块忘记改回来了。。以后写程序就要一口气写完,不要被外界干扰。

  1 #include <cmath>
  2 #include <queue>
  3 #include <cstdio>
  4 #include <cstring>
  5 using namespace std;
  6 
  7 const int maxn=1000000;
  8 
  9 int first1[maxn], first2[maxn], topo[maxn], topo2[maxn], out[maxn];
 10 int fa[maxn][30], dep[maxn], size[maxn], ans[maxn];
 11 struct Edge{
 12     int v, to, next;
 13 };
 14 Edge edges1[maxn], edges2[maxn];
 15 
 16 int n, cnte1, cnte2;
 17 
 18 void addedge(int *first, Edge *edges, int &cntedge,
 19              int v, int from, int to){
 20     edges[cntedge].v=v;
 21     edges[cntedge].to=to;
 22     edges[cntedge].next=first[from];
 23     first[from]=cntedge;
 24     cntedge++;
 25     return;
 26 }
 27 
 28 double log2(int x){
 29     return log(x)/log(2);
 30 }
 31 
 32 int get_lca(int x, int y){
 33     int t=0, step=1;
 34     if (dep[x]<dep[y]) swap(x, y);
 35     t=dep[x]-dep[y];
 36     while (t){
 37         if (t&1) x=fa[x][step];
 38         t>>=1;
 39         ++step;
 40     }
 41     for (int step=20; step>0; --step){
 42         if (fa[x][step]!=fa[y][step]){
 43             x=fa[x][step];
 44             y=fa[y][step];
 45         }
 46     }
 47     if (x!=y) x=fa[x][1];
 48     return x;
 49 }
 50 
 51 int main(){
 52     for (int i=0; i<maxn; ++i) first1[i]=first2[i]=-1;
 53     scanf("%d", &n);
 54     for (int i=1; i<=n; ++i){
 55         int t;
 56         scanf("%d", &t);
 57         while (t) {
 58             addedge(first1, edges1, cnte1, 1, i, t);
 59             addedge(first2, edges2, cnte2, 1, t, i);
 60             ++out[i];
 61             scanf("%d", &t);
 62         }
 63     }
 64     queue<int> q;
 65     for (int i=1; i<=n; ++i){
 66         if (!out[i]){
 67             addedge(first1, edges1, cnte1, 1, i, 0);
 68             addedge(first2, edges2, cnte2, 1, 0, i);
 69             out[i]=1;
 70         }
 71     }
 72     //以上:读入、加边
 73     q.push(0);
 74     int nownode, nowedge, nowson;
 75     for (int i=0; i<=n; ++i){
 76         nownode=q.front();
 77         q.pop();
 78         topo[i]=nownode;
 79         nowedge=first2[nownode];
 80         while (nowedge!=-1){
 81             nowson=edges2[nowedge].to;
 82             if (!--out[nowson]){
 83                 q.push(nowson);
 84             }
 85             nowedge=edges2[nowedge].next;
 86         }
 87     }
 88     //以上:拓扑排序
 89     int lca=0, cur=0;
 90     for (int i=1; i<=n; ++i){
 91         nownode=topo[i];
 92         nowedge=first1[nownode];
 93         lca=nowson=edges1[nowedge].to;
 94         nowedge=edges1[nowedge].next;
 95         while (nowedge!=-1){
 96             nowson=edges1[nowedge].to;
 97             //if (!nowedge) break; !!!!
 98             lca=get_lca(lca, nowson);
 99             nowedge=edges1[nowedge].next;
100         }
101         dep[nownode]=dep[lca]+1;
102         cur=0;
103         fa[nownode][cur]=nownode;
104         fa[nownode][++cur]=lca;
105         ++size[lca];
106         while (fa[nownode][cur]){
107             fa[nownode][cur+1]=fa[fa[nownode][cur]][cur];
108             ++cur;
109         }
110     }
111     //以上:建立灭绝树
112     while (!q.empty()) q.pop();
113     for (int i=0; i<=n; ++i){
114         ans[i]=1;
115         if (size[i]==0) q.push(i);
116     }
117     int fath;
118     for (int i=n; i>0; --i){
119         nownode=q.front();
120         q.pop();
121         fath=fa[nownode][1];
122         --size[fath];
123         if (!size[fath]) q.push(fath);
124         ans[fath]+=ans[nownode];
125     }
126     //以上:统计size
127     for (int i=1; i<=n; ++i)
128         printf("%d\n", ans[i]-1);
129     return 0;
130 }

附:用来对拍的python程序。

 1 from cyaron import *
 2 from random import randint
 3 
 4 for n in range(5, 11):
 5     input_io = IO()
 6     for i in range(0, 100):
 7             #对于n,生成100个数据。
 8         input_io.input_writeln(n)
 9         print(n) ###
10         topo = Vector.random(n, [(1, n)], 0)
11         for j in topo:
12             print(j, end=' ') ###
13             #拓扑序
14         print() ###
15         m = randint(n*2, n*3)
16             #有多少个边(有一些边会被舍弃掉)
17         out = []
18         for j in range(0, n):
19             out.append(0)
20             #每个结点的出度(吃什么)
21         for j in range(0, m):
22             t = randint(0, n-1)
23             if out[t] < t / 2:
24                 out[t] += 1
25         graph = []
26         for j in range(0, n):
27             graph.append([])
28         for j in range(1, n):
29             #对于每一个结点
30             use = []
31                 #连了哪些结点
32             for k in range(0, n):
33                 use.append(0)
34             for k in range(0, out[j]):
35                 #对于要连的边
36                 node = randint(0, j-1)
37                 while use[node] == 1:
38                     node = randint(0, j-1)
39                 use[node] = 1
40                 #选一个点
41                 graph[topo[j][0]-1].append(topo[node][0])
42         for j in graph:
43             for k in j:
44                 #print(k, end=" ")
45                 input_io.input_write(k)
46             #print(0)
47             input_io.input_writeln(0)
48         Compare.program("mine.exe", input=input_io,
49                         std_program="yangs.exe", stop_on_incorrect=True)

 

posted @ 2017-08-28 19:37  pechpo  阅读(193)  评论(0)    收藏  举报