「JLOI2014」松鼠的新家

蒟蒻我差点把简单问题复杂化QwQ。

传送门

洛谷P3258

BZOJ3631

题解

这两天刚做了几道树剖题。

害得我这题一上来就像码树剖。

其实完全没必要。

由于修改操作只有将一整条路劲上的点都加1,所以直接树上差分就可以了。

设修改的路径的两个端点分别为$a,b$,那么只要让a和b的权值加1,LCA(a,b)和它的父亲的权值分别减1。然后对于任意一个节点,它整个子树的权值加和便是该节点真正的权值。

然后注意一下细节即可。

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
#include<cstdio>
#include<algorithm>
using namespace std;
const int maxn=300005;
int n,A[maxn],tot,lnk[maxn],son[maxn*2],nxt[maxn*2],F[maxn],father[maxn][20],dep[maxn];
inline char nc()
{
const int S=131072;static char buf[S],*L,*R;
return L==R&&(R=(L=buf)+fread(buf,1,S,stdin),L==R)?EOF:*L++;
}
inline int read()
{
int ret=0,f=1;char ch=nc();
while(ch<'0'||ch>'9'){if(ch=='-')f=-f;ch=nc();}
while(ch>='0'&&ch<='9'){ret=ret*10+ch-'0';ch=nc();}
return ret*f;
}
inline void add_e(int x,int y){tot++;son[tot]=y;nxt[tot]=lnk[x];lnk[x]=tot;}
void Build(int now,int fa)
{
father[now][0]=fa;dep[now]=dep[fa]+1;
for(int i=1;i<=18;i++) father[now][i]=father[father[now][i-1]][i-1];
for(int i=lnk[now];i;i=nxt[i])
if(son[i]!=fa)
Build(son[i],now);
}
inline int LCA(int u,int v)
{
if(dep[u]<dep[v]) swap(u,v);
for(int i=18;i>=0;i--) if(dep[father[u][i]]>=dep[v]) u=father[u][i];
for(int i=18;i>=0;i--) if(father[u][i]!=father[v][i]){u=father[u][i];v=father[v][i];}
if(u!=v) return father[u][0];
return u;
}
void DFS(int now,int fa)
{
for(int i=lnk[now];i;i=nxt[i])
if(son[i]!=fa)
{DFS(son[i],now);F[now]+=F[son[i]];}
}
int main()
{
n=read();
for(int i=1;i<=n;i++) A[i]=read();
for(int i=1;i<n;i++)
{
int a=read(),b=read();
add_e(a,b);add_e(b,a);
}
Build(1,0);
for(int i=1;i<n;i++)
{
F[A[i]]++;
F[A[i+1]]++;
int lca=LCA(A[i],A[i+1]);
F[lca]--;
F[father[lca][0]]--;
}
DFS(1,0);
for(int i=1;i<n;i++) F[A[i+1]]--;
for(int i=1;i<=n;i++)
printf("%d\n",F[i]);
return 0;
}