小c同學認為跑步非常有趣,于是決定制作一款叫做《天天愛跑步》的遊戲。《天天愛跑步》是一個養成類遊戲,需要玩家每天按時上線,完成打卡任務。
這個遊戲的地圖可以看作一一棵包含 nn個結點和 n-1n−1條邊的樹, 每條邊連接配接兩個結點,且任意兩個結點存在一條路徑互相可達。樹上結點編号為從11到nn的連續正整數。
現在有mm個玩家,第ii個玩家的起點為 S_iS
i
,終點為 T_iT
。每天打卡任務開始時,所有玩家在第00秒同時從自己的起點出發, 以每秒跑一條邊的速度, 不間斷地沿着最短路徑向着自己的終點跑去, 跑到終點後該玩家就算完成了打卡任務。 (由于地圖是一棵樹, 是以每個人的路徑是唯一的)
小c想知道遊戲的活躍度, 是以在每個結點上都放置了一個觀察員。 在結點jj的觀察員會選擇在第W_jW
j
秒觀察玩家, 一個玩家能被這個觀察員觀察到當且僅當該玩家在第W_jW
秒也理到達了結點 jj 。 小C想知道每個觀察員會觀察到多少人?
注意: 我們認為一個玩家到達自己的終點後該玩家就會結束遊戲, 他不能等待一 段時間後再被觀察員觀察到。 即對于把結點jj作為終點的玩家: 若他在第W_jW
秒前到達終點,則在結點jj的觀察員不能觀察到該玩家;若他正好在第W_jW
秒到達終點,則在結點jj的觀察員可以觀察到這個玩家。
輸入輸出格式
輸入格式:
第一行有兩個整數nn和mm 。其中nn代表樹的結點數量, 同時也是觀察員的數量, mm代表玩家的數量。
接下來 n- 1n−1行每行兩個整數uu和 vv,表示結點 uu到結點 vv有一條邊。
接下來一行 nn個整數,其中第jj個整數為W_jW
, 表示結點jj出現觀察員的時間。
接下來 mm行,每行兩個整數S_iS
,和T_iT
,表示一個玩家的起點和終點。
對于所有的資料,保證1\leq S_i,T_i\leq n, 0\leq W_j\leq n1≤S
,T
≤n,0≤W
≤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
2 3
2 4
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]);
}
}