1. 程式人生 > >[BZOJ3712]Fiolki 重構樹(並查集)

[BZOJ3712]Fiolki 重構樹(並查集)

sample size 不知道 沈澱 過程 表示 deep hellip long

3712: [PA2014]Fiolki

Time Limit: 30 Sec Memory Limit: 128 MB

Description

化學家吉麗想要配置一種神奇的藥水來拯救世界。
吉麗有n種不同的液體物質,和n個藥瓶(均從1到n編號)。初始時,第i個瓶內裝著g[i]克的第i種物質。吉麗需要執行一定的步驟來配置藥水,第i個步驟是將第a[i]個瓶子內的所有液體倒入第b[i]個瓶子,此後第a[i]個瓶子不會再被用到。瓶子的容量可以視作是無限的。
吉麗知道某幾對液體物質在一起時會發生反應產生沈澱,具體反應是1克c[i]物質和1克d[i]物質生成2克沈澱,一直進行直到某一反應物耗盡。生成的沈澱不會和任何物質反應。當有多於一對可以發生反應的物質在一起時,吉麗知道它們的反應順序。每次傾倒完後,吉麗會等到反應結束後再執行下一步驟。

吉麗想知道配置過程中總共產生多少沈澱。

Input

第一行三個整數n,m,k(0<=m<n<=200000,0<=k<=500000),分別表示藥瓶的個數(即物質的種數),操作步數,可以發生的反應數量。
第二行有n個整數g[1],g[2],…,g[n](1<=g[i]<=10^9),表示初始時每個瓶內物質的質量。
接下來m行,每行兩個整數a[i],b[i](1<=a[i],b[i]<=n,a[i]≠b[i]),表示第i個步驟。保證a[i]在以後的步驟中不再出現。
接下來k行,每行是一對可以發生反應的物質c[i],d[i](1<=c[i],d[i]<=n,c[i]≠d[i]),按照反應的優先順序給出。同一個反應不會重復出現。

Output

Sample Input

3 2 1
2 3 4
1 2
3 2
2 3

Sample Output

6 考試的時候,上來憑直覺覺得是並查集……
此後第a[i]個瓶子不會再被用到.
怎麽看怎麽是並查集嘛,然後……打了個鏈表的O(玄學)暴力,就開始想怎麽優化查找的過程 可是到考試結束也沒想出來-_-最後只好交暴力了 回了宿舍Troywardalao問我題面是什麽(考試時候他都沒看題,然而考完試他10min就渺掉了233) 然後兩個人想了10min就做出來了...(Torywar dalao為什麽你想正解想得這麽熟練啊)
我們考慮這個倒藥品的過程,很顯然倒藥品的順序會產生影響. 如果我們按照普通的並查集一樣合並,會產生一系列問題,最大的問題就是不知道有哪些反應 我們考慮每次合並新建節點並向他們連邊(也就是重構樹,參見http://www.cnblogs.com/LadyLex/p/7275821.html) 這樣,我們就把每一次合並拆開,並且獨立考慮每次合並的過程 接下來使用了類似離線的思想,我們首先處理一個計算lca的方式(tarjan,倍增隨意了),然後考慮:
對於每一對可以反應的物質,他們可能,也只可能在他們重構樹上lca處反應
既然這樣,我們就可以把每個反應添加到他們的lca上 隨後,在掃一遍重構時新添加到點,也就是執行一遍倒藥品的過程,並且處理可能發生的反應 總的復雜度是O(m+nlogn+klogn+k),分別對應重構,ST表預處理,向lca添加反應,處理合並過程 (ps:其實還是有一點遺憾,明明自己已經打過重構樹的題了,考試時候卻沒想到.看來我對知識的掌握程度還沒有到那麽熟練的程度....聯賽之前一定要把已經學的知識掌握紮實啊...) 代碼見下:
 1 #include <cstdio>
 2 #include <cstring>
 3 #include <algorithm>
 4 #include <vector>
 5 using namespace std;
 6 const int N=200010,K=500010;
 7 typedef long long LL;
 8 LL sum;
 9 int n,m,k,w[N],fa[N<<1],bin[25];
10 int e,adj[N<<1],cnt,f[N<<1][19],deep[N<<1];
11 int find(int a){return fa[a]==a?a:fa[a]=find(fa[a]);}
12 inline int min(int a,int b){return a<b?a:b;}
13 struct data{int a,b;data(int x=0,int y=0){a=x,b=y;}}step[N];
14 struct edge{int zhong,next;}s[N<<1];
15 vector<data>re[N<<1];
16 inline void add(int qi,int zhong)
17     {s[++e].zhong=zhong;s[e].next=adj[qi];adj[qi]=e;}
18 void dfs(int rt)
19 {
20     deep[rt]=deep[f[rt][0]]+1;
21     for(int i=adj[rt];i;i=s[i].next)
22         dfs(s[i].zhong);
23 }
24 inline void ST()
25 {
26     for(int i=1;i<=18;i++)
27         for(int j=1;j<=cnt;j++)
28             f[j][i]=f[f[j][i-1]][i-1];
29 }
30 inline int LCA(int a,int b)
31 {
32     if(deep[a]<deep[b])swap(a,b);
33     int cha=deep[a]-deep[b];
34     for(int j=18;~j;j--)
35         if(cha&bin[j])a=f[a][j];
36     if(a==b)return a;
37     for(int j=18;~j;j--)
38         if(f[a][j]!=f[b][j])a=f[a][j],b=f[b][j];
39     return f[a][0];
40 }
41 int main()
42 {
43     bin[0]=1;for(int i=1;i<=20;i++)bin[i]=bin[i-1]<<1;
44     int a,b,lca;scanf("%d%d%d",&n,&m,&k);cnt=n;
45     for(int i=1;i<=n+m;i++)fa[i]=i;
46     for(int i=1;i<=n;i++)scanf("%d",&w[i]);
47     for(int i=1;i<=m;i++)
48         scanf("%d%d",&step[i].a,&step[i].b),
49         a=find(step[i].a),b=find(step[i].b),
50         fa[a]=fa[b]=f[a][0]=f[b][0]=++cnt,
51         add(cnt,a),add(cnt,b);
52     ST();
53     for(int i=1;i<=cnt;i++)
54         if(!deep[i])dfs(find(i));
55     for(int i=1;i<=k;i++)
56     {
57         scanf("%d%d",&a,&b);
58         if(find(a)==find(b))
59             lca=LCA(a,b),re[lca].push_back(data(a,b));
60     }
61     for(int i=n+1;i<=cnt;i++)
62         for(int j=0,len=re[i].size();j<len;j++)
63             a=min(w[re[i][j].a],w[re[i][j].b]),
64             w[re[i][j].a]-=a,w[re[i][j].b]-=a,sum+=a;
65     printf("%lld\n",sum<<1);
66 }

[BZOJ3712]Fiolki 重構樹(並查集)