做法一:对于每一个点的修改,顺序改变一下是不会影响结果的。我们离线做,可以一个点一个点的修改。
(还是过不了啊,仍然T)qwq。
做法二:
我们尽量把实际的操作搞成标记,不操作,以降低复杂度。
我们用三个数组实现。
pushup[x]表示x周围的点对x的影响,tag[x]记录的是x这个点操作的次数,sontag[x]表示的是x所有的儿子节点的操作次数。
那么修改时:
pushup[x]+=siz[x]
p
u
s
h
u
p
[
x
]
+
=
s
i
z
[
x
]
tag[x]++,sontag[f[x]]++
t
a
g
[
x
]
+
+
,
s
o
n
t
a
g
[
f
[
x
]
]
+
+
pushup[f[x]]+=2(x和f[x]都会加一)
p
u
s
h
u
p
[
f
[
x
]
]
+
=
2
(
x
和
f
[
x
]
都
会
加
一
)
pushup[f[f[x]]]+=1
p
u
s
h
u
p
[
f
[
f
[
x
]
]
]
+
=
1
查询时:
ans+=pushup[x]+2∗tag[f[x]]+tag[f[f[x]]]+sontag[f[x]]−tag[x]
a
n
s
+
=
p
u
s
h
u
p
[
x
]
+
2
∗
t
a
g
[
f
[
x
]
]
+
t
a
g
[
f
[
f
[
x
]
]
]
+
s
o
n
t
a
g
[
f
[
x
]
]
−
t
a
g
[
x
]
分别是自己上面操作时产生的和,父亲每操作一次自己的和就会+2(父亲和自己个占1),爷爷每操作一次自己会+1(只有父亲的1),加上父亲的其他儿子每操作一次就会使父亲+1,父亲又在x周围。
总的来说就是操作自己的贡献+操作其他点对自己产生的贡献。
时间复杂度O(n+m)。
需要读入优化。
#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>
#include<algorithm>
#define LL long long
using namespace std;
const int N=1e5+77;
int n,m,siz[N],f[N];
LL ans,pushup[N],tag[N],sontag[N];
long long read()
{
long long res = 0;
char ch = 0;
while (ch < '0' || ch > '9')
ch = getchar();
while (ch >= '0' && ch <= '9')
{
res = res * 10 + ch - '0';
ch = getchar();
}
return res;
}
int main()
{
freopen("sample.in","r",stdin);
freopen("sample.out","w",stdout);
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++) siz[i]++;
for(int x,i=2;i<=n;i++)
{
x=read();
f[i]=x;
siz[i]++;siz[x]++;
}
for(int x,i=1;i<=m;i++)
{
x=read();
pushup[x]+=siz[x];
pushup[f[x]]+=2;
pushup[f[f[x]]]+=1;
tag[x]++;
sontag[f[x]]++;
LL s=0;
s+=pushup[x]+2*tag[f[x]]+tag[f[f[x]]]+sontag[f[x]]-tag[x];
ans+=s;
}
printf("%lld\n",ans);
return 0;
}