歌名 - 歌手
0:00

    【NOIP2016提高A组五校联考4】label

    题目

    这里写图片描述

    题目

    20%算法

    \(f_{i,j}\)表示第i个节点选了j这个权值的方案数。
    显然转移方程为,$$f_{i,j}=\Pi_{v=son(i)}(\sum_{k=1}{j-k}f_{v,k}+\sum_{k=j+k}f_{v,k})$$

    40%算法

    接着上面的想法,
    观察转移方程,发现,求和部分其实是两段连续的,那么将\(f_{i}\)求一个前缀和。

    100%算法

    观察\(f\)数组,发现其实\(f\)是对称的,而且中间的一段是相同的,设深度为x,那么前面就有\((x-1)k\)个不同,然后中间有一段相同,后面又有\((x-1)k\)个不同。
    那么就可以只求出前\((x-1)k+1\)个,前缀和就可以直接算出来(程序中我直接求出前10010个的值,没有计算x)。

    #include <iostream>
    #include <cstdio>
    #include <cstdlib>
    #include <cstring>
    #include <algorithm>
    #include <queue>
    const long long maxlongint=2147483647;
    const long long mo=1000000007;
    const long long N=105;
    using namespace std;
    long long f[N][N*N*2],_,n,m,k,next[N*2],last[N*2],to[N*2],tot,same[N],sum1[N][N*N],sum2[N][N*N];
    long long pos[N];
    long long max(long long x,long long y)
    {
    	if(x<y) return y;else return x;
    }
    long long min(long long x,long long y)
    {
    	if(x>y) return y;else return x;
    }
    long long bj(long long x,long long y)
    {
    	next[++tot]=last[x];
    	last[x]=tot;
    	to[tot]=y;
    }
    long long sum(int x,int y)
    {
    	if(y<=10010) return sum1[x][y];
    	else
    	if(y<m-pos[x]+1)
    	{
    		return (sum1[x][pos[x]]+same[x]*(y-pos[x])%mo)%mo;
    	}
    	else
    	{
    		return (sum1[x][pos[x]]+(m-pos[x]-pos[x])*same[x]%mo+sum2[x][(y-(m-pos[x]))])%mo;
    	}
    }
    long long dg(long long x,long long fa)
    {
    	bool q=false;
    	for(long long i=last[x];i;i=next[i])
    	{
    		long long j=to[i];
    		if(j!=fa)
    		{
    			dg(j,x);
    			q=true;
    		}
    	}
    	for(long long i=1;i<=min(10010,m);i++) f[x][i]=1;
    	for(long long i=last[x];i;i=next[i])
    	{
    		long long j=to[i];
    		if(j!=fa)
    		{
    			for(long long l=1;l<=min(10010,m);l++)
    			{
    				long long t=0;
    				t=(t+(sum(j,max(0,l-k))-sum(j,0)+mo))%mo;
    				if(l+k<=m) 
    				{
    					t=(t+(sum(j,m)-sum(j,l+k-1)+mo))%mo;
    					if(max(0,l-k)==l+k)
    						t=(t-f[j][l+k]+mo)%mo;
    				}
    				f[x][l]=(f[x][l]*t)%mo;
    			}
    		}
    	}
    	if(10010<m)
    	{
    		for(int i=1;i<=10010;i++)
    		{
    			if(f[x][i]==f[x][i+1])
    			{
    				same[x]=f[x][i];
    				pos[x]=i-1;
    				break;
    			}
    		}	
    	}
    	else
    	{
    		same[x]=f[x][m/2];
    	}
    	for(int i=1;i<=min(10010,m);i++)
    		sum1[x][i]=(sum1[x][i-1]+f[x][i])%mo;
    	for(int i=1;i<=min(10010,m);i++)
    		sum2[x][i]=(sum2[x][i-1]+f[x][pos[x]-i+1])%mo;
    }
    int main()
    {
    	scanf("%lld",&_);
    	while(_--)
    	{
    		scanf("%lld%lld%lld",&n,&m,&k);
    		memset(f,0,sizeof(f));
    		memset(last,0,sizeof(last));
    		memset(next,0,sizeof(next));
    		memset(sum1,0,sizeof(sum1));
    		memset(sum2,0,sizeof(sum2));
    		memset(pos,0,sizeof(pos));
    		tot=0;
    		for(long long i=1;i<=n-1;i++)
    		{
    			long long x,y;
    			scanf("%lld%lld",&x,&y);
    			bj(x,y);
    			bj(y,x);
    		}
    		dg(1,0);
    		printf("%lld\n",sum(1,m));
    	}
    }
    
    
    posted @ 2018-05-20 22:50  无尽的蓝黄  阅读(117)  评论(0编辑  收藏  举报