洛谷P3258 [JLOI2014]松鼠的新家——題解
阿新 • • 發佈:2019-01-10
題目傳送門
題目大意:
給出一個在樹上移動的序列,求出每個點被經過的次數。
思考過程:
維護兩個點之間的鏈的資訊很明顯我們需要樹剖,但是樹剖無法維護鏈上每個節點的資訊,所以我們需要藉助差分陣列。
具體做法:
1.樹剖
2.對於序列中相鄰的兩個點,像普通樹剖求答案那樣往上跳到LCA,過程中將兩個節點的差分陣列起點+1,終點-1,注意重複的情況,用tag陣列來記錄
程式碼:
#include <bits/stdc++.h>
using namespace std;
const int maxn=3e5+1000;
struct stu
{
int to,next;
}road[maxn*2 ]; int first[maxn],cnt=0;
int dep[maxn],cnum[maxn],id[maxn],dfn[maxn],top[maxn],fa[maxn],hson[maxn],cf[maxn],sum[maxn],tag[maxn],a[maxn];
int n,nowtop;
void addedge(int x,int y)
{
road[++cnt].to=y;
road[cnt].next=first[x];
first[x]=cnt;
}
void dfs1(int now,int depth)
{
int maxx=0;
dep[now]=depth;
cnum[now]=1 ;
for(int i=first[now];i;i=road[i].next)
{
int to=road[i].to;
if(to==fa[now]) continue;
fa[to]=now;
dfs1(to,depth+1);
cnum[now]+=cnum[to];
if(cnum[to]>maxx)
{
maxx=cnum[to];
hson[now]=to;
}
}
}
void dfs2(int now,bool what)
{
if(what==1) nowtop=now;
top[now]=nowtop;
id[now]=++cnt;
dfn[cnt]=now;
if(!hson[now]) return;
dfs2(hson[now],0);
for(int i=first[now];i;i=road[i].next)
{
int to=road[i].to;
if(to==fa[now]||to==hson[now]) continue;
dfs2(to,1);
}
}
void calcu(int x,int y)
{
while(top[x]!=top[y])
{
if(dep[top[x]]<dep[top[y]]) swap(x,y);
cf[id[top[x]]]++;cf[id[x]]--;
tag[x]++;
x=fa[top[x]];
}
if(dep[x]<dep[y]) swap(x,y);
cf[id[y]]++;cf[id[x]]--;
tag[x]++;
}
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++) scanf("%d",&a[i]);
for(int i=1;i<=n-1;i++)
{
int x,y;
scanf("%d%d",&x,&y);
addedge(x,y);addedge(y,x);
}
dfs1(a[1],1);
cnt=0;
dfs2(a[1],1);
calcu(a[1],a[2]);
for(int i=2;i<=n-1;i++)
{
calcu(a[i],a[i+1]);
tag[a[i]]--;
}
tag[a[n]]--;
sum[1]=cf[1];
for(int i=2;i<=n;i++) sum[i]=sum[i-1]+cf[i];
for(int i=1;i<=n;i++) printf("%d\n",sum[id[i]]+tag[i]);
return 0;
}