2018牛客网暑假ACM多校训练赛(第二场)E tree 动态规划

原文链接https://www.cnblogs.com/zhouzhendong/p/NowCoder-2018-Summer-Round2-E.html

题目传送门 - 2018牛客多校赛第二场 E

题意

  一棵 $n$ 个结点的树,每个点有一个点权,有 $m$ 次操作,每次操作有三种:

  1.  修改一个点的点权

  2.  修改一个点的父亲

  3.  询问包含某个点的所有大小为 $c$ 的连通块的点权乘积之和。

  $n,m\leq 100000,c\leq10 $

题解

    

  

  
  以上题解图片摘自 laofu 题解。

 

  我再画几幅图介绍一下:

  注:箭头表示箭头尾端节点的 dp 对箭头指向节点有 dp 贡献。

    虚箭头表示中间省略一些有箭头连接的节点。

    虚线表示中线省略一些节点,但是没有箭头连接。

    每个图,最上面的节点表示往上走 $10$ 步到达的祖先。

    最下面的节点表示操作中的 $x$ 节点。

    注意一下修改和询问里面各有一个特殊箭头,意义自己理解。

 

 

 

 

 

 

 

  

  

 

代码

#include <bits/stdc++.h>
using namespace std;
int read(){
	char ch=getchar();
	int x=0;
	while (!isdigit(ch))
		ch=getchar();
	while (isdigit(ch))
		x=(x<<1)+(x<<3)+ch-48,ch=getchar();
	return x;
}
const int N=100005,mod=1e9+7;
int n,m,val[N],fa[N];
int dp[N][11];
int Pow(int x,int y){
	int ans=1;
	for (;y;y>>=1,x=1LL*x*x%mod)
		if (y&1)
			ans=1LL*ans*x%mod;
	return ans;
}
void DP(int a,int b){
	if (!a||!b)
		return;
	for (int i=10;i>=1;i--)
		for (int j=1;j<i;j++)
			dp[a][i]=(1LL*dp[a][i]+1LL*dp[a][j]*dp[b][i-j])%mod;
}
void IDP(int a,int b){
	if (!a||!b)
		return;
	for (int i=1;i<=10;i++)
		for (int j=1;j<i;j++)
			dp[a][i]=(1LL*dp[a][i]-1LL*dp[a][j]*dp[b][i-j])%mod;
}
int main(){
	n=read(),m=read();
	memset(dp,0,sizeof dp);
	for (int i=1;i<=n;i++)
		dp[i][1]=val[i]=read();
	for (int i=2;i<=n;i++)
		fa[i]=read();
	for (int i=n;i>1;i--)
		DP(fa[i],i);
	while (m--){
		int opt=read(),x=read(),y=read(),anc[15];
		if (opt==0){
			for (int i=1,j=x;i<=10;i++,j=fa[j])
				anc[i]=j;
			for (int i=10;i>1;i--)
				IDP(anc[i],anc[i-1]);
			int inv=Pow(val[x],mod-2);
			val[x]=y;
			for (int i=1;i<=10;i++)
				dp[x][i]=1LL*dp[x][i]*inv%mod*y%mod;
			for (int i=1;i<10;i++)
				DP(anc[i+1],anc[i]);
		}
		if (opt==1){
			for (int i=1,j=x;i<=10;i++,j=fa[j])
				anc[i]=j;
			for (int i=10;i>1;i--)
				IDP(anc[i],anc[i-1]);
			for (int i=2;i<10;i++)
				DP(anc[i+1],anc[i]);
			fa[x]=y;
			for (int i=1,j=x;i<=10;i++,j=fa[j])
				anc[i]=j;
			for (int i=10;i>2;i--)
				IDP(anc[i],anc[i-1]);
			for (int i=1;i<10;i++)
				DP(anc[i+1],anc[i]);
		}
		if (opt==2){
			for (int i=1,j=x;i<=10;i++,j=fa[j])
				anc[i]=j;
			for (int i=10;i>1;i--)
				IDP(anc[i],anc[i-1]);
			for (int i=10;i>1;i--)
				DP(anc[i-1],anc[i]);
			printf("%d\n",(dp[x][y]+mod)%mod);
			for (int i=1;i<10;i++)
				IDP(anc[i],anc[i+1]);
			for (int i=1;i<10;i++)
				DP(anc[i+1],anc[i]);
		}
	}
	return 0;
}

  

posted @ 2018-07-22 19:16  zzd233  阅读(681)  评论(0编辑  收藏  举报