zoukankan      html  css  js  c++  java
  • [正睿集训2021] 模拟赛2

    计算器

    咕咕咕

    二叉树

    题目描述

    有一棵无穷大的完全二叉树 (T),任意一个点都有两个子节点。给定一棵有限的二叉树 (G),满足除叶节点外每个节点都有恰好两个儿子,你需要将 (G) 的每个节点分别对应到 (T) 的节点上面去,使得对于 (G) 的叶节点深度为 (h[i]),对于 (G) 的非叶节点的两个子节点 (ls,rs),需要分别在 (T) 的对应节点的左右子树上。

    求对应的方案数,对 (1e9+7) 取模。

    (nleq 5000,h_ileq 10^9)

    解法

    我们考虑 (G),可以在树上 (dp),设 (f[i][j]) 表示点 (i) 被放在 (T)(j) 深度上,并考虑其子树的放置方案数,转移就枚举左右儿子放置的深度:

    [f(i,j)=(sum_{k>j}f(l,k)2^{k-j-1})(sum_{k>j}f(r,k)2^{k-j-1}) ]

    (g(i,j)=sum_{kgeq j}f(i,k)2^k),则 (dp) 方程可以写成 (f(i,j)=g(l,j+1)g(r,j+1)2^{-2j-2}),这样算法就是 (O(nh_i)) 的。

    后面真的是神仙操作了,我们想沿用 (dp) 的思路,但是奈何深度这一维太大了。这时候我们可以拿出多项式来优化,我们一般是把范围比较大的信息设置为多项式的变量,这样通过一些多项式的变化来使得它不计入复杂度,而一些范围较小的信息是可以枚举的。

    可以归纳证明(g(i,j)) 一定是形如 (a_0+a_12^{-j}+a_22^{-2j}....+a_k2^{-kj}) 的形式(但是 (j) 必须在合法范围内),说得更明确一点,这个 (a) 就是只和 (i) 有关的系数,如果我们想要 (g(i,j)) 就把 (j) 带进这个柿子就行了。

    这一步我还不是特别理解,因为归纳法只能证明却不能告诉我们为什么有这个结论。主要还是领会多项式的思想,以后可能会补充为什么。

    下面来证明给出的结论,设 (b)(g(l))(g(r)) 的各项系数的卷积,因为 (g) 相乘可以看成系数卷积,设 (s_i)(g(i,j)) 的合法范围,下面的 (t) 表示卷积后的项数:

    [egin{align}g(i,j)&=sum_{k=j}^{s_i}2^kf(i,k)\&=sum_{k=j}^{s_i}2^kcdot2^{-2k-2}g(l,k+1)g(r,k+1)\&=sum_{k=j}^{s_i}2^{-k-2}sum_{l=0}^{t}b_l2^{-l(k+1)}\&=frac{1}{2}sum_{l=0}^{t}b_lcdot2^{-l-1}sum_{k=j}^{s_i}2^{-(l+1)k}\&=frac{1}{2}sum_{l=0}^tb_lcdot 2^{-l-1}frac{2^{-(s_i+1)(l+1)}-2^{-j(l+1)}}{2^{-l-1}-1}end{align} ]

    多项式的变量就是 (j),和 (j) 无关的可以放到常数那里,所以卷积完的结果有 (2^{-j(l+1)}),那么就归到多项式系数的第 (l+1) 项即可。现在我们的复杂度就和深度没关系了,每次暴力卷积算 (b) 即可,由于每两个点只会在 (lca) 处有 (1) 的贡献(又来了),所以时间复杂度 (O(n^2))

    #include <cstdio>
    #include <cstring>
    #include <iostream>
    using namespace std;
    const int M = 5005;
    const int MOD = 1e9+7;
    #define int long long
    int read()
    {
    	int x=0,f=1;char c;
    	while((c=getchar())<'0' || c>'9') {if(c=='-') f=-1;}
    	while(c>='0' && c<='9') {x=(x<<3)+(x<<1)+(c^48);c=getchar();}
    	return x*f;
    }
    int n,inv,ans,ls[M],rs[M],h[M],a[M][M],b[M],siz[M];
    int qkpow(int a,int b)
    {
    	int r=1;
    	while(b>0)
    	{
    		if(b&1) r=r*a%MOD;
    		a=a*a%MOD;
    		b>>=1;
    	}
    	return r;
    }
    void dfs(int u)
    {
    	if(!ls[u])
    	{
    		siz[u]=1;
    		a[u][0]=qkpow(2,h[u]);
    		return ;
    	}
    	dfs(ls[u]);
    	dfs(rs[u]);
    	h[u]=min(h[ls[u]],h[rs[u]])-1;//算j的范围
    	if(h[u]<0) return ;
    	siz[u]=siz[ls[u]]+siz[rs[u]];
    	memset(b,0,sizeof b);
    	for(int i=0;i<siz[ls[u]];i++)//暴力卷积
    		for(int j=0;j<siz[rs[u]];j++)
    			b[i+j]=(b[i+j]+a[ls[u]][i]*a[rs[u]][j])%MOD;
    	for(int i=0;i<siz[u];i++)
    	{
    		int x=qkpow(2,(MOD-1)-i-1);
    		int fk=qkpow(x-1,MOD-2);//分母
    		b[i]=b[i]*qkpow(inv,i+1)%MOD;
    		a[u][i+1]-=fk%MOD*b[i]%MOD;
    		a[u][0]=(a[u][0]+qkpow(x,h[u]+1)*fk%MOD*b[i])%MOD;
    	}
    	for(int i=0;i<siz[u];i++)
    		a[u][i]=a[u][i]*inv%MOD;
    }
    signed main()
    {
    	n=read();inv=(MOD+1)/2;
    	for(int i=1;i<=n;i++)
    	{
    		int op=read();
    		if(op==0) ls[i]=read(),rs[i]=read();
    		else h[i]=read();
    	}
    	dfs(1);
    	for(int i=0;i<siz[1];i++)
    		ans=(ans+a[1][i])%MOD;
    	printf("%lld
    ",(ans+MOD)%MOD);
    }
    
    

    后缀数组

    不好意思又咕了。

  • 相关阅读:
    java 深克隆(深拷贝)与浅克隆(拷贝)详解
    设计模式之单例模式
    设计模式之工厂模式
    批量下载google 字体小工具
    LBPL--基于Asp.net、 quartz.net 快速开发定时服务的插件化项目
    测试
    WCF 生产json对外的接口
    四舍五入小算法 (以前写的,采用拆分)
    自己动手写控件(模仿mvc htmlhelper的类)
    步骤详解安装Apache web服务器
  • 原文地址:https://www.cnblogs.com/C202044zxy/p/14456880.html
Copyright © 2011-2022 走看看