luogu U95602 射手座之日
先考虑一个简单容斥:设 \(val(x)\) 表示 \(x\) 子树内所有点作为 \(LCA\) 的贡献之和,那么以 \(x\) 为 \(LCA\) 的贡献就是 \(val(x)-\sum\limits_{v\in son\ of\ x}val(v)\)。
现在的问题就是如何求 \(val(x)\) 。我们发现,在把 \(x\) 子树内每个点在关卡表都标注出来后,总贡献就是 \(\sum\limits_{len}\frac{len\times (len+1)}{2}\),其中 \(len\) 表示现在关卡表中一个极长子段的长度。
遇到对每个子树都要处理的题目,我们很自然的想到了 \(dsu\ on\ tree\),当然这里还有一个很妙的小技巧:对于每个节点 \(x\),我们在标记它的子树时使用 \(dfn_x\),然后在重儿子处理过后直接赋值 \(dfn_x=dfn_{height\_son[x]}\),这样就能省去很多很多麻烦的撤销操作(我一开始还准备写可撤销并查集来着)。
代码:
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
typedef long long LL;
const int N=200009;
int n,head[N],cnt,p[N],a[N],son[N],siz[N],L[N],R[N],l[N],r[N],Index,DFN[N],rev[N],vis[N],now;
LL ans[N],Ans,s[N];
struct Edge
{
int nxt,to;
}g[N*2];
void add(int from,int to)
{
g[++cnt].nxt=head[from];
g[cnt].to=to;
head[from]=cnt;
}
void init()
{
scanf("%d",&n);
for (int i=2,x;i<=n;i++)
{
scanf("%d",&x);
add(x,i),add(i,x);
}
for (int i=1,x;i<=n;i++)
scanf("%d",&x),a[x]=i;
for (int i=1;i<=n;i++)
scanf("%d",&p[i]);
}
void dfs(int x,int fa)
{
siz[x]=1,DFN[x]=++Index,rev[Index]=x;
for (int i=head[x];i;i=g[i].nxt)
{
int v=g[i].to;
if(v==fa)
continue;
dfs(v,x);
siz[x]+=siz[v];
if(siz[son[x]]<siz[v])
son[x]=v;
}
L[x]=DFN[x],R[x]=Index;
}
LL calc(int x) { return 1ll*x*(x+1)/2; }
void Insert(int x,int k)
{
vis[x]=now,l[x]=r[x]=x;
if(vis[x+1]!=now) l[x+1]=r[x+1]=0;
if(vis[x-1]!=now) l[x-1]=r[x-1]=0;
int xl=0,xr=0;
if(l[x-1]&&r[x+1])
{
xl=x-l[x-1],xr=r[x+1]-x;
r[l[x-1]]=r[x+1],l[r[x+1]]=l[x-1];
}
else if(l[x-1])
{
xl=x-l[x-1];
r[l[x-1]]=x,l[x]=l[x-1];
}
else if(r[x+1])
{
xr=r[x+1]-x;
l[r[x+1]]=x,r[x]=r[x+1];
}
ans[k]=ans[k]-calc(xl)-calc(xr)+calc(xl+xr+1);
}
void DFS(int x,int fa)
{
for (int i=head[x];i;i=g[i].nxt)
{
int v=g[i].to;
if(v==fa||v==son[x])
continue;
DFS(v,x);
}
now=DFN[x];
if(son[x])
{
DFS(son[x],x);
ans[x]+=ans[son[x]];
DFN[x]=now=DFN[son[x]];
for (int i=head[x];i;i=g[i].nxt)
{
int v=g[i].to;
if(v==fa||v==son[x])
continue;
for (int j=L[v];j<=R[v];j++)
Insert(a[rev[j]],x);
}
}
Insert(a[x],x);
LL k=ans[x];
for (int i=head[x];i;i=g[i].nxt)
{
int v=g[i].to;
if(v==fa) continue;
k-=ans[v];
}
Ans+=k*p[x];
}
void work()
{
dfs(1,-1);
DFS(1,-1);
printf("%lld\n",Ans);
}
int main()
{
init();
work();
return 0;
}
由于博主比较菜,所以有很多东西待学习,大部分文章会持续更新,另外如果有出错或者不周之处,欢迎大家在评论中指出!