洛谷P1600 每天愛跑步(線段樹合併)

小c同窗認爲跑步很是有趣,因而決定製做一款叫作《每天愛跑步》的遊戲。《每天愛跑步》是一個養成類遊戲,須要玩家天天按時上線,完成打卡任務。c++

這個遊戲的地圖能夠看做一一棵包含 nn個結點和 n-1n−1條邊的樹, 每條邊鏈接兩個結點,且任意兩個結點存在一條路徑互相可達。樹上結點編號爲從11到nn的連續正整數。spa

如今有mm個玩家,第ii個玩家的起點爲 S_iS
i
​ ,終點爲 T_iT
i
​ 。天天打卡任務開始時,全部玩家在第00秒同時從本身的起點出發, 以每秒跑一條邊的速度, 不間斷地沿着最短路徑向着本身的終點跑去, 跑到終點後該玩家就算完成了打卡任務。 (因爲地圖是一棵樹, 因此每一個人的路徑是惟一的)code

小c想知道遊戲的活躍度, 因此在每一個結點上都放置了一個觀察員。 在結點jj的觀察員會選擇在第W_jW
j
​ 秒觀察玩家, 一個玩家能被這個觀察員觀察到當且僅當該玩家在第W_jW
j
​ 秒也理到達告終點 jj 。 小C想知道每一個觀察員會觀察到多少人?blog

注意: 咱們認爲一個玩家到達本身的終點後該玩家就會結束遊戲, 他不能等待一 段時間後再被觀察員觀察到。 即對於把結點jj做爲終點的玩家: 若他在第W_jW
j
​ 秒前到達終點,則在結點jj的觀察員不能觀察到該玩家;若他正好在第W_jW
j
​ 秒到達終點,則在結點jj的觀察員能夠觀察到這個玩家。遊戲

輸入輸出格式
輸入格式:
第一行有兩個整數nn和mm 。其中nn表明樹的結點數量, 同時也是觀察員的數量, mm表明玩家的數量。it

接下來 n- 1n−1行每行兩個整數uu和 vv,表示結點 uu到結點 vv有一條邊。class

接下來一行 nn個整數,其中第jj個整數爲W_jW
j
​ , 表示結點jj出現觀察員的時間。地圖

接下來 mm行,每行兩個整數S_iS
i
​ ,和T_iT
i
​ ,表示一個玩家的起點和終點。數據

對於全部的數據,保證1\leq S_i,T_i\leq n, 0\leq W_j\leq n1≤S
i
​ ,T
i
​ ≤n,0≤W
j
​ ≤n 。查詢

輸出格式:
輸出1行 nn個整數,第jj個整數表示結點jj的觀察員能夠觀察到多少人。

輸入輸出樣例
輸入樣例#1:
6 3
2 3
1 2
1 4
4 5
4 6
0 2 5 1 2 3
1 5
1 3
2 6
輸出樣例#1:
2 0 0 1 1 1
輸入樣例#2:
5 3
1 2
2 3
2 4
1 5
0 1 0 3 0
3 1
1 4
5 5
輸出樣例#2:
1 2 1 0 1

 

題解:號稱提升組最難一題,其實難度還行

考慮把一我的的路徑拆成起點到lca和lca到終點兩段,差分一下用線段樹維護

具體的操做是對起點插入deep起點,終點插入2*deep[lca]-deep起點,至關於把起點沿lca翻上去。

而後線段樹合併一波就搞定了

查詢的是每一個點deep+wj和deep-wj距離的點有幾個

其實線段樹合併是大材小用了,若是對每一個點查詢li-ri時間之間有多少人通過顯然才更妙

 

代碼以下:

#include<bits/stdc++.h>
#define lson tr[now].l
#define rson tr[now].r
using namespace std; struct tree { int l,r,sum; } tr[20000010]; vector<int> g[300010]; vector<int> op1[300010],op2[300010]; int n,m,ans[300010],q[300010],rt[300010],deep[300010],fa[300010][20],cnt; int N=600000; int dfs(int now,int f,int dep) { deep[now]=dep; fa[now][0]=f; rt[now]=now; ++cnt; for(int i=1; i<=19; i++) { fa[now][i]=fa[fa[now][i-1]][i-1]; } for(int i=0; i<g[now].size(); i++) { if(g[now][i]==f) continue; dfs(g[now][i],now,dep+1); } } int lca(int x,int y) { if(deep[x]<deep[y]) swap(x,y); for(int i=19; i>=0; i--) { if(deep[fa[x][i]]>=deep[y]) x=fa[x][i]; } if(x==y) return x; for(int i=19; i>=0; i--) { if(fa[x][i]!=fa[y][i]) x=fa[x][i],y=fa[y][i]; } return fa[x][0]; } int push_up(int now) { tr[now].sum=tr[lson].sum+tr[rson].sum; } int insert(int &now,int l,int r,int pos,int val) { if(!now) now=++cnt; if(l==r) { tr[now].sum+=val; return 0; } int mid=(l+r)>>1; if(pos<=mid) { insert(lson,l,mid,pos,val); } else { insert(rson,mid+1,r,pos,val); } push_up(now); } int query(int now,int l,int r,int pos) { if(l==r) return tr[now].sum; int mid=(l+r)>>1; if(pos<=mid) return query(lson,l,mid,pos); else return query(rson,mid+1,r,pos); } int merge(int a,int b,int l,int r) { if(!b) return a; if(!a) return b; if(l==r) { tr[a].sum+=tr[b].sum; return a; } int mid=(l+r)>>1; tr[a].l=merge(tr[a].l,tr[b].l,l,mid); tr[a].r=merge(tr[a].r,tr[b].r,mid+1,r); push_up(a); return a; } int solve(int now,int f) { for(int i=0; i<op1[now].size(); i++) { insert(rt[now],0,N,op1[now][i],1); } for(int i=0; i<op2[now].size(); i++) { insert(rt[now],0,N,op2[now][i],-1); } for(int i=0; i<g[now].size(); i++) { if(g[now][i]==f) continue; solve(g[now][i],now); merge(rt[now],rt[g[now][i]],0,N); } if(deep[now]+n-q[now]>=0) ans[now]+=query(rt[now],0,N,deep[now]+n-q[now]); if(deep[now]+n+q[now]<=N&&q[now]!=0) ans[now]+=query(rt[now],0,N,deep[now]+n+q[now]); } int main() { int from,to; scanf("%d%d",&n,&m); for(int i=1; i<n; i++) { scanf("%d%d",&from,&to); g[from].push_back(to); g[to].push_back(from); } for(int i=1; i<=n; i++) scanf("%d",&q[i]); dfs(1,0,1); for(int i=1; i<=m; i++) { scanf("%d%d",&from,&to); int anc=lca(from,to); op1[from].push_back(deep[from]+n); op1[to].push_back(n-(deep[from]-deep[anc])+deep[anc]); op2[anc].push_back(deep[from]+n); op2[fa[anc][0]].push_back(n-(deep[from]-deep[anc])+deep[anc]); } solve(1,0); for(int i=1;i<=n;i++) { printf("%d ",ans[i]); } }
相關文章
相關標籤/搜索