wannalfy 挑战赛8 F 白云的树(树形dp)

链接:https://www.nowcoder.com/acm/contest/57/F

时间限制:C/C++ 2秒,其他语言4秒

空间限制:C/C++ 262144K,其他语言524288K
64bit IO Format: %lld

题目描

白云有一棵n个结点的树,以1号结点为根,这棵树的每个结点有一个权值val i
定义一个连通块的喜爱度为块内所有结点权值的乘积。
白兔有很多疑问,每个疑问有两个参数k,s,表示询问所有经过点k的大小为s的连通块的喜爱度之和。
白云会定期对树做一些修改。

输入描述:

第一行1个整数n,Q,表示树的结点个数和事件个数。
第二行n个整数表示val
1 ... n

第三行n-1个整数表示2...n号结点的父亲。
接下来Q行,第一个数为op∈{0,1},
如果op=0表示白云要对某个结点的权值进行修改,接下来两个数k,c表示把结点k的权值修改为c。
如果op=1表示白兔的疑问,接下来两个数k,s。
 

输出描述:

 对于op=1,每行一个数表示答案。答案对1e9+7
取模。
示例1

输入

15 15
6 4 8 6 8 9 10 9 2 9 8 3 3 6 2
1 1 1 3 1 3 6 5 3 10 9 7 9 13
1 13 8
1 2 4
1 12 8
1 11 2
0 2 9
0 11 4
1 3 6
0 4 5
1 13 7
1 8 2
1 7 6
1 7 8
0 5 5
0 1 7
1 1 6

输出

128592000
11304
35520768
72
10402608
16325280
81
6030720
359079840
8686888

备注:

结点k的父亲为1...k-1的一个均匀随机整数。
val i∈[1,10 9+7),s≤10,n,Q≤10^5
题解:

//

  1 #include <bits/stdc++.h>
  2 #define mst(a,b)    memset((a),(b), sizeof a)
  3 #define lowbit(a)   ((a)&(-a))
  4 #define IOS         ios::sync_with_stdio(0);cin.tie(0);
  5 #define MP          make_pair
  6 using namespace std;
  7 typedef long long ll;
  8 typedef pair<int,int> pii;
  9 const int mod=1e9+7;
 10 const int maxn=1e5+10;
 11 ll dp[maxn][11];
 12 int val[maxn],fa[maxn];
 13 vector<int>son[maxn];
 14 int qpow(ll a,int b){
 15     ll res=1;
 16     while(b){
 17         if(b&1)res=res*a%mod;
 18         b>>=1;
 19         a=a*a%mod;
 20     }
 21     return res;
 22 }
 23 void add(int a,int b){
 24     for(int i=10;i;--i)
 25         for(int j=1;j<i;++j)
 26             dp[a][i]=(dp[a][i]+dp[b][j]*dp[a][i-j])%mod;
 27 
 28 }
 29 void del(int a,int b){
 30     for(int i=1;i<=10;++i)
 31         for(int j=1;j<i;++j)
 32             dp[a][i]=(dp[a][i] - dp[b][j]*dp[a][i-j])%mod;
 33 }
 34 void dfs(int pos){
 35     dp[pos][0]=1;dp[pos][1]=val[pos];
 36     for(int i=0;i<son[pos].size();++i){
 37         int to=son[pos][i];
 38         dfs(to);
 39         add(pos,to);
 40     }
 41 }
 42 void update_1(int pos){
 43     if(pos==1)return;
 44     update_1(fa[pos]);
 45     del(fa[pos],pos);
 46 }
 47 void update_2(int pos){
 48     if(pos==1)return;
 49     add(fa[pos],pos);
 50     update_2(fa[pos]);
 51 }
 52 void update(int k,int c){
 53     update_1(k);
 54     int cc=qpow(val[k],mod-2);
 55     for(int i=1;i<=10;++i)
 56         dp[k][i]=dp[k][i]*cc%mod*c%mod;
 57     val[k]=c;
 58     update_2(k);
 59 }
 60 ll dd[2][11];
 61 int now;
 62 void qq(int pos,int s){
 63     if(pos!=1)qq(fa[pos],pos);
 64     now^=1;
 65     memcpy(dd[now],dp[pos],sizeof dp[pos]);
 66     for(int i=1;i<=10;++i)
 67         for(int j=1;j<i;++j)
 68             dd[now][i]=(dd[now][i] - dp[s][j]*dd[now][i-j])%mod;
 69     if(pos!=1){
 70         for(int i=10;i;--i)
 71             for(int j=1;j<i;++j)
 72                 dd[now][i]=(dd[now][i]+dd[now^1][j]*dd[now][i-j])%mod;
 73     }
 74 }
 75 int query(int k,int s){
 76     if(k==1)return (dp[k][s]+mod)%mod;
 77     int f=fa[k];
 78     qq(f,k);
 79     ll ret=0;
 80     for(int i=0;i<s;++i)
 81         ret=(ret+dd[now][i]*dp[k][s-i])%mod;
 82     return (ret+mod)%mod;
 83 }
 84 int main(){
 85 #ifdef local
 86 freopen("in.txt","r",stdin);
 87 //freopen("out.txt","w",stdout);
 88 #endif
 89     int n,q;scanf("%d%d",&n,&q);
 90     for(int i=1;i<=n;++i)scanf("%d",&val[i]);
 91     for(int i=2;i<=n;++i){
 92         scanf("%d",&fa[i]);
 93         son[fa[i]].push_back(i);
 94     }
 95     dfs(1);
 96     while(q--){
 97         int op,a,b;scanf("%d%d%d",&op,&a,&b);
 98         if(op)printf("%d\n",query(a,b));
 99         else update(a,b);
100     }
101     return 0;
102 }

首先是随机树的树高是logn这点是不知道的,所以看到题就是蒙圈的,这点是学到了

然后在码的过程中update那个函数,里面对于k点dp值的更新我一开始是这样的

   for(int i=1;i<=10;++i){
        dp[k][i]=(dp[k][i]-val[k]*dp[k][i-1])%mod;
        dp[k][i]=(dp[k][i]+mod)%mod;
    }
    val[k]=c;
    for(int i=10;i;--i){
        dp[k][i]=(dp[k][i]+dp[k][i-1]*val[k])%mod;
    }

错的很离谱,引以为鉴

 

转载于:https://www.cnblogs.com/bibibi/p/8454490.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值