BZOJ4719 [Noip2016]天天愛跑步


本文版權歸ljh2000和博客園共有,歡迎轉載,但須保留此聲明,並給出原文鏈接,謝謝合作。

 

本文作者:ljh2000
作者博客:http://www.cnblogs.com/ljh2000-jump/
轉載請注明出處,侵權必究,保留最終解釋權!

 

 

Description

小c同學認為跑步非常有趣,於是決定制作一款叫做《天天愛跑步》的游戲。天天愛跑步是一個養成類游戲,需要
玩家每天按時上線,完成打卡任務。這個游戲的地圖可以看作一一棵包含 N個結點和N-1 條邊的樹, 每條邊連接兩
個結點,且任意兩個結點存在一條路徑互相可達。樹上結點編號為從1到N的連續正整數。現在有個玩家,第個玩家的
起點為Si ,終點為Ti  。每天打卡任務開始時,所有玩家在第0秒同時從自己的起點出發, 以每秒跑一條邊的速度,
不間斷地沿着最短路徑向着自己的終點跑去, 跑到終點后該玩家就算完成了打卡任務。 (由於地圖是一棵樹, 所以
每個人的路徑是唯一的)小C想知道游戲的活躍度, 所以在每個結點上都放置了一個觀察員。 在結點的觀察員會選
擇在第Wj秒觀察玩家, 一個玩家能被這個觀察員觀察到當且僅當該玩家在第Wj秒也理到達了結點J  。 小C想知道
每個觀察員會觀察到多少人?注意: 我們認為一個玩家到達自己的終點后該玩家就會結束游戲, 他不能等待一 段時
間后再被觀察員觀察到。 即對於把結點J作為終點的玩家: 若他在第Wj秒重到達終點,則在結點J的觀察員不能觀察
到該玩家;若他正好在第Wj秒到達終點,則在結點的觀察員可以觀察到這個玩家。

Input

第一行有兩個整數N和M 。其中N代表樹的結點數量, 同時也是觀察員的數量, M代表玩家的數量。
接下來n-1 行每行兩個整數U和V ,表示結點U 到結點V 有一條邊。
接下來一行N 個整數,其中第個整數為Wj , 表示結點出現觀察員的時間。
接下來 M行,每行兩個整數Si和Ti,表示一個玩家的起點和終點。
對於所有的數據,保證 。
1<=Si,Ti<=N,0<=Wj<=N
 

Output

輸出1行N 個整數,第個整數表示結點的觀察員可以觀察到多少人。

 

Sample Input

6 3
2 3
1 2
1 4
4 5
4 6
0 2 5 1 2 3
1 5
1 3
2 6

Sample Output

1 2 1 0 1

HINT

 

對於1號點,Wi=0,故只有起點為1號點的玩家才會被觀察到,所以玩家1和玩家2被觀察到,共有2人被觀察到。

對於2號點,沒有玩家在第2秒時在此結點,共0人被觀察到。

對於3號點,沒有玩家在第5秒時在此結點,共0人被觀察到。

對於4號點,玩家1被觀察到,共1人被觀察到。

對於5號點,玩家1被觀察到,共1人被觀察到。

對於6號點,玩家3被觀察到,共1人被觀察到。
 
 
正解:lca+統計
解題報告:

 

25分

  考慮此時n很小,可以對於每條路徑上暴力模擬,經過某個點時可以看一下當前時刻,是否跟經過的點的w相等,如果相等,則貢獻加一。

 

45分

  注意到測試點9-12時,保證m條路徑的出發點都是1,那么我們可以考慮如果將1作為樹根,那么一條路徑怎樣才能對於它經過的點產生貢獻。不難看出對於一個點i,只有在deep[i]=w[i],才有可能有貢獻。我在考場上是直接用的鏈剖+線段樹,因為這就變成模板題了,而且n不到10w,盡管復雜度偏高,但是不易錯。直接對於每條路徑經過的點在線段樹上增加1次經過次數,顯然只有deep與w相等的點才會產生貢獻。

  事實上對於S=1的情況有線性的算法,正解會詳細介紹,不再贅述。

 

60分

  注意到測試點6-8時,題目保證樹退化成鏈。我們觀察一下對於鏈而言,有什么特別的地方。首先要明確,此時m條路徑在鏈上肯定是要么往左要么往右,即S<=T或者S>T。先只考慮S<=T的情況,如果對於S到T之間的點i,要產生貢獻的話,肯定滿足i-S=w[i],移項可得S=i-w[i]時才可以滿足要求。注意到等式右邊只與i本身有關,不妨設為K[i],所以題目變成了查詢S到T之間K[i]等於S的i的數量。因為題目只涉及到首和尾,我們可以很容易聯想到差分,即對於S打上+1標記,T打上-1標記。

  根據上述思路,我們考慮具體做法:對於每個點i,我們很容易發現只有從一個特定的點出發才有可能對i產生貢獻。我們考慮維護一個統計數組A,A[k]表示的是處理到當前的結點時,從k出發的路徑(而且還沒有走到終點)有多少條。這樣對於每個點i,我們只要查詢一下所對應的A[K[i]]就可以了,根據上面的分析,這就是我們的答案了。有一點注意處理:一個點i時,我們需要把以i為起點的路徑加入統計數組A,再計算這個結點的貢獻,最后再把以這個結點為終點的路徑從A中消除,具體可以用vector實現(上述處理順序的必要性仔細想想就很容易想通了)。

  而對於S>T的情況完全類似,只是需要把K[i]定義為i+w[i],其余做法完全類似。

      

100分

  題目中設計的幾個檔次的部分分其實暗示已經很明顯了。鏈的做法離正解就不遠了。而S=1和T=1是在告訴我們什么呢?拆路徑!很容易發現,一條S到T的路徑可以拆成一條S到LCA的路徑和LCA到T的路徑,然后對於這兩條路徑,一條往上,一條往下,都可以對應成鏈的處理方式了!

  考慮對於每條路徑,先將其拆分成兩條路徑(為了簡化對LCA在兩條路徑中都出現的各種情況,我們可以先就讓LCA出現兩次,如果最后發現LCA是有貢獻的,只需-1即可),同樣,我們先只考慮向上的路徑。如果我們對於S在統計數組A上打上1的標記,LCA在統計數組A上打上-1的標記,那么題目轉化為求一個點的子樹和。考慮上述做法正確性:因為只有S到LCA路徑之間的點會產生貢獻,而當這個點位於路徑之間時,子樹和會產生1的貢獻,而在S的子樹中或者LCA的上方都不會產生貢獻。具體實現呢?對於一個點i,產生貢獻的條件是deep[S]-deep[i]=w[i],同樣令K[i]=deep[i]+w[i],當我們dfs到i時查詢A[k[i]]的值即為貢獻。為了保證正確性,我們思考統計答案的方式和順序。首先我們肯定是在處理完i的子樹之后再來處理i(想想就知道了),然后我們需要再把以i出發的向上的路徑加入統計數組,再進行查詢,最后把以i為終點的路徑所產生的貢獻在統計數組A中消除即可。注意到我們上面維護的僅僅是一個點的深度,由於同一深度的點很多,所以我們查詢的時候會發現會把不在同一子樹的點統計入答案,那怎么辦呢?我們考慮對於一個點要查詢子樹和,肯定是只要單獨地考慮這一個子樹的貢獻,所以我們可以記錄進入i時A[k[i]]的值,再在訪問完i的子樹之后統計答案時,看一下此時新的A[k[i]]的值,容易發現新的值減掉進入時的,才是真正的i的子樹中的A[k[i]]的值。這樣我們就可以避免把別的子樹的答案統計進來了。

  對於向下的點做法類似,有一點復雜的地方就是等式變成了deep[T]-deep[i]=len-w[i](len為路徑長度),發現如果這樣做的話會出現負數,那么我們就把統計數組向右平移30w位就可以了。

  上述做法如果采用的是倍增求LCA的話,復雜度就是O(nlogn);如果用tarjan離線求LCA的話,可以做到O(n+m)。

 

 

 注意事項

  對於樹上每個結點,統計答案時不能直接查詢在統計數組中的對應的路徑條數,而應該統計dfs進入i時,和訪問完i的子樹時的變化量。

 
 
 
  O(nlogn):
 1 //It is made by ljh2000
 2 #include <iostream>
 3 #include <cstdlib>
 4 #include <cstring>
 5 #include <cstdio>
 6 #include <cmath>
 7 #include <algorithm>
 8 #include <ctime>
 9 #include <vector>
10 #include <queue>
11 #include <map>
12 #include <set>
13 #include <string>
14 using namespace std;
15 typedef long long LL;
16 const int MAXN = 300011;
17 const int MAXM = 600011; 
18 int n,m,ecnt,first[MAXN],next[MAXM],to[MAXM],f[MAXN][20],deep[MAXN],ans[MAXN],val[MAXN],tong[MAXN],MAXD,w[MAXN],num[1000011];
19 vector<int>ljh[MAXN],ljh2[MAXN],ljh3[MAXN];
20 struct node{ int s,t,lca,len;}a[MAXN];
21 inline int getint(){
22     int w=0,q=0; char c=getchar(); while((c<'0'||c>'9') && c!='-') c=getchar();
23     if(c=='-') q=1,c=getchar(); while (c>='0'&&c<='9') w=w*10+c-'0',c=getchar(); return q?-w:w;
24 }
25 inline void link(int x,int y){ next[++ecnt]=first[x]; first[x]=ecnt; to[ecnt]=y; }
26 inline void init(int x,int fa){ for(int i=first[x];i;i=next[i]) { int v=to[i]; if(v==fa) continue; deep[v]=deep[x]+1; init(v,x); f[v][0]=x; } }
27 inline int lca(int x,int y){
28     if(deep[x]<deep[y]) swap(x,y); int t=0; while((1<<t)<=deep[x]) t++; t--;
29     for(int i=t;i>=0;i--) if(deep[x]-(1<<i)>=deep[y]) x=f[x][i]; if(x==y) return y;
30     for(int i=t;i>=0;i--) if(f[x][i]!=f[y][i]) x=f[x][i],y=f[y][i]; return f[x][0];
31 }
32 
33 inline void dfs(int x,int fa){
34     int now=w[x]+deep[x],cun; if(now<=MAXD) cun=tong[now];
35     for(int i=first[x];i;i=next[i]) {
36         int v=to[i]; if(v==fa) continue;
37         dfs(v,x);
38     }
39     tong[deep[x]]+=val[x]; if(now<=MAXD) ans[x]=tong[now]-cun;
40     for(int i=0,ss=ljh[x].size();i<ss;i++) tong[deep[ljh[x][i]]]--;
41 }
42 
43 inline void DFS(int x,int fa){
44     int now=deep[x]-w[x],cun; now+=300000; cun=num[now];
45     for(int i=first[x];i;i=next[i]) {
46         int v=to[i]; if(v==fa) continue;
47         DFS(v,x);
48     }
49     for(int i=0,ss=ljh2[x].size();i<ss;i++) num[300000+ljh2[x][i]]++;
50     ans[x]+=num[now]-cun;
51     for(int i=0,ss=ljh3[x].size();i<ss;i++) num[300000+ljh3[x][i]]--;
52 }
53 
54 inline void work(){  
55     n=getint(); m=getint(); int x,y; for(int i=1;i<n;i++) { x=getint(); y=getint(); link(x,y); link(y,x); }
56     for(int i=1;i<=n;i++) w[i]=getint(); deep[1]=1; init(1,0); for(int i=1;i<=n;i++) MAXD=max(MAXD,deep[i]);
57     for(int j=1;j<=19;j++) for(int i=1;i<=n;i++) f[i][j]=f[f[i][j-1]][j-1]; 
58     for(int i=1;i<=m;i++) {
59         a[i].s=getint(),a[i].t=getint(),val[a[i].s]++;
60         a[i].lca=lca(a[i].s,a[i].t),a[i].len=deep[a[i].s]+deep[a[i].t]-deep[a[i].lca]*2;
61         ljh[a[i].lca].push_back(a[i].s);
62     }
63     dfs(1,0); 
64     for(int i=1;i<=m;i++) {
65         ljh2[a[i].t].push_back(deep[a[i].t]-a[i].len);
66         ljh3[a[i].lca].push_back(deep[a[i].t]-a[i].len);
67     }
68     DFS(1,0);
69     for(int i=1;i<=m;i++) if(deep[a[i].s]-deep[a[i].lca]==w[a[i].lca]) ans[a[i].lca]--;
70     for(int i=1;i<=n;i++) { printf("%d",ans[i]); if(i<n) printf(" "); }
71 }
72 
73 int main()
74 {
75     work();
76     return 0;
77 }

 

 
 
  O(n+m):
 1 //It is made by ljh2000
 2 #include <iostream>
 3 #include <cstdlib>
 4 #include <cstring>
 5 #include <cstdio>
 6 #include <cmath>
 7 #include <algorithm>
 8 #include <ctime>
 9 #include <vector>
10 #include <queue>
11 #include <map>
12 #include <set>
13 #include <string>
14 using namespace std;
15 typedef long long LL;
16 const int MAXN = 300011;
17 const int MAXM = 600011; 
18 int n,m,ecnt,first[MAXN],next[MAXM],to[MAXM],f[MAXN][20],deep[MAXN],ans[MAXN],val[MAXN],tong[MAXN],MAXD,w[MAXN],num[1000011];
19 int head[MAXN],tt[MAXM],nn[MAXM],father[MAXN],vis[MAXN];
20 vector<int>ljh[MAXN],ljh2[MAXN],ljh3[MAXN];
21 struct node{ int s,t,lca,len;}a[MAXN];
22 inline int getint(){
23     int w=0,q=0; char c=getchar(); while((c<'0'||c>'9') && c!='-') c=getchar();
24     if(c=='-') q=1,c=getchar(); while (c>='0'&&c<='9') w=w*10+c-'0',c=getchar(); return q?-w:w;
25 }
26 inline void link(int x,int y){ next[++ecnt]=first[x]; first[x]=ecnt; to[ecnt]=y; }
27 inline void LINK(int x,int y){ nn[++ecnt]=head[x]; head[x]=ecnt; tt[ecnt]=y; }
28 inline int find(int x){ if(father[x]!=x) father[x]=find(father[x]); return father[x]; }
29 inline void init(int x,int fa){ 
30     father[x]=x; vis[x]=1;
31     for(int i=head[x];i;i=nn[i]) {
32         int v=tt[i];
33         if(x==a[v].s&&vis[a[v].t]) a[v].lca=find(a[v].t);
34         if(x==a[v].t&&vis[a[v].s]) a[v].lca=find(a[v].s);
35     }
36     for(int i=first[x];i;i=next[i]) {
37         int v=to[i]; if(v==fa) continue; 
38         deep[v]=deep[x]+1; init(v,x); father[v]=x;
39         f[v][0]=x; 
40     }    
41 }
42 
43 inline int lca(int x,int y){
44     if(deep[x]<deep[y]) swap(x,y); int t=0; while((1<<t)<=deep[x]) t++; t--;
45     for(int i=t;i>=0;i--) if(deep[x]-(1<<i)>=deep[y]) x=f[x][i]; if(x==y) return y;
46     for(int i=t;i>=0;i--) if(f[x][i]!=f[y][i]) x=f[x][i],y=f[y][i]; return f[x][0];
47 }
48 
49 inline void dfs(int x,int fa){
50     int now=w[x]+deep[x],cun; if(now<=MAXD) cun=tong[now];
51     for(int i=first[x];i;i=next[i]) {
52         int v=to[i]; if(v==fa) continue;
53         dfs(v,x);
54     }
55     tong[deep[x]]+=val[x]; if(now<=MAXD) ans[x]=tong[now]-cun;
56     for(int i=0,ss=ljh[x].size();i<ss;i++) tong[deep[ljh[x][i]]]--;
57 }
58 
59 inline void DFS(int x,int fa){
60     int now=deep[x]-w[x],cun; now+=300000; cun=num[now];
61     for(int i=first[x];i;i=next[i]) {
62         int v=to[i]; if(v==fa) continue;
63         DFS(v,x);
64     }
65     for(int i=0,ss=ljh2[x].size();i<ss;i++) num[300000+ljh2[x][i]]++;
66     ans[x]+=num[now]-cun;
67     for(int i=0,ss=ljh3[x].size();i<ss;i++) num[300000+ljh3[x][i]]--;
68 }
69 
70 inline void work(){  
71     n=getint(); m=getint(); int x,y; for(int i=1;i<n;i++) { x=getint(); y=getint(); link(x,y); link(y,x); }
72     for(int i=1;i<=n;i++) w[i]=getint(); ecnt=0;
73     for(int i=1;i<=m;i++) { a[i].s=getint(),a[i].t=getint(),val[a[i].s]++; LINK(a[i].s,i); LINK(a[i].t,i);}
74     deep[1]=1; init(1,0); for(int i=1;i<=n;i++) MAXD=max(MAXD,deep[i]);
75     for(int j=1;j<=19;j++) for(int i=1;i<=n;i++) f[i][j]=f[f[i][j-1]][j-1]; 
76     for(int i=1;i<=m;i++) {
77         a[i].len=deep[a[i].s]+deep[a[i].t]-deep[a[i].lca]*2;
78         ljh[a[i].lca].push_back(a[i].s);
79     }
80     dfs(1,0); 
81     for(int i=1;i<=m;i++) {
82         ljh2[a[i].t].push_back(deep[a[i].t]-a[i].len);
83         ljh3[a[i].lca].push_back(deep[a[i].t]-a[i].len);
84     }
85     DFS(1,0);
86     for(int i=1;i<=m;i++) if(deep[a[i].s]-deep[a[i].lca]==w[a[i].lca]) ans[a[i].lca]--;
87     for(int i=1;i<=n;i++) { printf("%d",ans[i]); if(i<n) printf(" "); }
88 }
89 
90 int main()
91 {
92     work();
93     return 0;
94 }

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM