• luogu U95602 射手座之日


    先考虑一个简单容斥:设 (val(x)) 表示 (x) 子树内所有点作为 (LCA) 的贡献之和,那么以 (x)(LCA) 的贡献就是 (val(x)-sumlimits_{vin son of x}val(v))

    现在的问题就是如何求 (val(x)) 。我们发现,在把 (x) 子树内每个点在关卡表都标注出来后,总贡献就是 (sumlimits_{len}frac{len imes (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
    ",Ans);
    }
    
    int main()
    {
    	init();
    	work();
    	return 0;
    }
    
    由于博主比较菜,所以有很多东西待学习,大部分文章会持续更新,另外如果有出错或者不周之处,欢迎大家在评论中指出!
  • 相关阅读:
    android system.img
    ab压力测试和CC预防
    ubuntu工具积累
    ViewManager
    PopupWindow
    singleton注意
    java byte[]生成
    java有符号无符号的转换
    C#垃圾回收Finalize 和Dispose的理解
    Silverlight 获取汉字拼音首字母
  • 原文地址:https://www.cnblogs.com/With-penguin/p/13191253.html
Copyright © 2020-2023  润新知