zoukankan      html  css  js  c++  java
  • 洛谷 P4099

    emmm... tzc 说是好题,那肯定是好题,因为 tzc 英语带师!

    洛谷题目页面传送门

    给定一个基图为树的有向图,求它的拓扑序个数。答案对 (10^9+7) 取模。本题多测。

    (nin[1,1000],Tin[1,5])

    考虑树形 DP。很自然的想到 (dp_{i,j}) 表示子树 (i) 中节点 (i) 在拓扑序列中的序数为 (j) 的拓扑序列数。

    那么在转移的时候,要把一个节点的所有儿子都考虑进去,这个极不好控制,是指数级的。不难发现,儿子与儿子之间没有直接联系,只是 (i) 与儿子们各有一个联系。于是可以将儿子一个一个算入转移中,而非同时转移,这样能使时间复杂度得以控制在多项式级别。具体地,大概就加个一维 (dp_{i,j,k})(j) 表示考虑到 (i) 的第 (j) 个儿子了,(k) 为原 (j)。这样看起来状态数增加了一级,其实不然,因为 (sumlimits_{i=1}^n|son_i|=mathrm O(n))。还是 (mathrm O!left(n^2 ight))

    目标:(sumlimits_{i=1}^ndp_{1,|son_1|,i});边界:(dp_{i,0,1}=1)

    接下来考虑如何转移。当前要计算 (dp_{i,j}),我们设 (h=dp_{i,j},f=dp_{i,j-1},g=dp_{son_{i,j},left|son_{son_{i,j}} ight|}),那么显然是 (f)(g) 共同算出 (h)。然后设 (sz_h,sz_f,sz_g) 分别为所对应的 size。根据 (i)(son_{i,j}) 之间边的方向,分两种情况,分别为在拓扑序中 (son_{i,j})(i) 前和后。现在先讨论前者。

    考虑枚举 (h) 中在 (i) 前面有几个 (g) 中的点。那么此时 (i)(f) 中的排名已经确定了,用在 (h) 中的排名减去前面在 (g) 中的点数即可。那么 (son_{i,j})(g) 中的排名呢?它需要在 (i) 前面,于是在 (1) 到「(h) 中在 (i) 前面有几个 (g) 中的点」均可。(f)(g) 内部的排列方案数定下来了,然后再很自然地给 (f)(g) 分配相对位置,(i) 左边和右边各乘上一个组合数即可。于是得到前者的状态转移方程:

    [h_i=sum_{j=1}^{sz_g}f_{i-j}sum_{k=1}^jg_kinom{i-1}jinom{sz_h-i}{sz_g-j} ]

    类似地可以得到后者的方程(比较恶心一点):

    [h_i=sum_{j=1}^{sz_g}f_{i+j-sz_g}sum_{k=sz_g-j+1}^{sz_g}g_kinom{sz_h-i}jinom{i-1}{sz_g-j} ]

    此时总时间复杂度是 (mathrm O!left(n^4 ight)) 的(平方状态,平方转移)。考虑优化。

    显然,两个方程中的第二个 (sum) 可以用前缀和优化掉,此时三方,继续优化。然后定睛一看:写成前缀和形式之后,展开组合数,发现一派常数一派关于 (j) 一派关于 (i-j),那不就是个卷积吗?!我当时害怕极了,我不会 FFT 呀!而且即使优化了,也是平方对数,还要乘个 (T),也是过不去的样子。

    然后发现这个三方的复杂度很虚伪,它实际上是 (mathrm O!left(sumlimits_{i=1}^nsumlimits_{j=1}^{|son_i|}sz_{son_{i,j}}sumlimits_{k=1}^{j-1}sz_{son_{i,k}} ight)),很跑不满三方。然后拿链、菊花、蒲公英、毛毛虫、随机树都试一遍,发现没有一个被卡掉的。事实上它是平方的。下面归纳证明 (mathrm T(n)leq n^2)

    1. 显然 (mathrm T(0)=0)
    2. 假设 (forall iin[0,x),mathrm T(i)leq i^2)。设大小为 (x) 的树的树根的儿子子树大小分别为 (a_{1sim m}),则显然有 (mathrm T(x)=sumlimits_{i=1}^mmathrm T(a_i)+sumlimits_{i=1}^ma_ileft(sumlimits_{j=1}^{i-1}a_j+1 ight))。根据假设有 (mathrm T(x)leq sumlimits_{i=1}^ma_i^2+sumlimits_{i=1}^ma_ileft(sumlimits_{j=1}^{i-1}a_j+1 ight)=sumlimits_{i=1}^ma_ileft(sumlimits_{j=1}^ia_j+1 ight)leq sumlimits_{i=1}^ma_ixleq x^2)

    综上,得证。

    代码(要很多次取模,常数比较大,要开 (mathrm O_2)):

    #include<bits/stdc++.h>
    using namespace std;
    #define mp make_pair
    #define X first
    #define Y second
    #define pb push_back
    const int mod=1000000007;
    const int N=1000;
    int n;
    int comb[N+1][N+1];
    vector<pair<int,bool> > nei[N+1];
    int dp[N+1][N+2];
    int nw[N+1];
    int sz[N+1];
    void dfs(int x=1,int fa=0){
    	sz[x]=1;
    	dp[x][1]=1;
    	for(int i=0;i<nei[x].size();i++){
    		int y=nei[x][i].X;bool z=nei[x][i].Y;
    		if(y==fa)continue;
    		dfs(y,x);
    		sz[x]+=sz[y];
    		if(z){
    			for(int j=1;j<=sz[y];j++)(dp[y][j]+=dp[y][j-1])%=mod;
    			for(int j=1;j<=sz[x];j++)nw[j]=0;
    			for(int j=1;j<=sz[x];j++)for(int k=0;k<=sz[y]&&k<j;k++)
    				(nw[j]+=1ll*dp[x][j-k]*dp[y][k]%mod*comb[j-1][k]%mod*comb[sz[x]-j][sz[y]-k]%mod)%=mod;
    			for(int j=1;j<=sz[x];j++)dp[x][j]=nw[j];
    		}
    		else{
    			for(int j=sz[y];j;j--)(dp[y][j]+=dp[y][j+1])%=mod;
    			for(int j=1;j<=sz[x];j++)nw[j]=0;
    			for(int j=1;j<=sz[x];j++)for(int k=0;k<=sz[y]&&k<=sz[x]-j;k++)
    				(nw[j]+=1ll*dp[x][j+k-sz[y]]*dp[y][sz[y]-k+1]%mod*comb[sz[x]-j][k]%mod*comb[j-1][sz[y]-k]%mod)%=mod;
    			for(int j=1;j<=sz[x];j++)dp[x][j]=nw[j];
    		}
    	}
    }
    void mian(){
    	cin>>n;
    	for(int i=1;i<=n;i++)nei[i].clear();
    	memset(dp,0,sizeof(dp));
    	for(int i=1;i<n;i++){
    		int x,y;char z;
    		cin>>x>>z>>y;
    		x++,y++;
    		if(z=='<')nei[x].pb(mp(y,0)),nei[y].pb(mp(x,1));
    		else nei[x].pb(mp(y,1)),nei[y].pb(mp(x,0));
    	}
    	dfs();
    	int ans=0;
    	for(int i=1;i<=n;i++)(ans+=dp[1][i])%=mod;
    	cout<<ans<<"
    ";
    }
    int main(){
    	comb[0][0]=1;
    	for(int i=1;i<=N;i++)for(int j=0;j<=i;j++)comb[i][j]=((j?comb[i-1][j-1]:0)+(j<i?comb[i-1][j]:0))%mod;
    	int testnum;
    	cin>>testnum;
    	while(testnum--)mian();
    	return 0;
    }
    
    珍爱生命,远离抄袭!
  • 相关阅读:
    JSONP
    懒加载
    HTTP 状态代码
    java4中创建内对象的方法
    注册jdbc驱动程序的三种方式
    java Clone()克隆
    Class.forName()的理解
    Bitmap介绍
    前端-PC端瀑布流【10张图】
    百度小程序-图片画廊-使用previewImage方法实现
  • 原文地址:https://www.cnblogs.com/ycx-akioi/p/Luogu-P4099.html
Copyright © 2011-2022 走看看