zoukankan      html  css  js  c++  java
  • [***]HZOI20190714 T2熟练剖分

    这题真的神仙,蒟弱表示看题解看不懂……std看了几个小时大概看懂,还有一些细节的东西没有思考。

    最难受的是题解和std好像并不是同一个人写的……数组状态不一样……看了好久才看出来f也是前缀和。

    F[i][j]表示在点 i 为根的子树中,向下最长轻链长度小于等于 j 的概率。

    首先递归下去并求出子树大小,然后枚举重儿子,枚举该点最长轻链长度,再次枚举儿子节点并逐个考虑(感觉和概率充电器(特地从考试总结中搬出来了)那个题有点类似),

    假设当前枚举的重儿子是v(i),枚举到儿子节点v(j),x最长轻链长度为k,设gs为v(j)之前考虑的儿子中最长轻链长度为k的概率(因为是前缀和,所以代码中有减这个操作,f同理),如果v(j)=v(i)即v(j)为重儿子,则设fs为以v(j)为根的子树最长轻链长度为k的概率,f[x][k]=gs*f[v(j)][k](v(j)之前考虑的儿子为长度k*以v(j)为根字数长度<=k(此条边为重链所以可以等于))+fs*g[k]-gs*fs(去重),

    如果v(j)是轻儿子,则设fs为以v(j)为根的子树最长轻链长度为k-1的概率,f[x][k]=gs*f[v(j)][k-1]+fs*g[k]-gs*fs,大致同上,只是x与v(j)相连的这条边为轻链所以有减1,值得提醒的一点是这里的f[x][k]并不是最终的f[x][k],只是考虑到当前几个儿子时的值,一个儿子一个儿子地向里加。考虑到f数组直接改的话会错,所以用h数组保存,最后加到g数组中清空h,当v(i)为重儿子这个情况考虑玩后将g数组加到f中去,清空g。当前节点x求完后,此时的f数组并不是前缀和,所以需要再次转化。

    最后求答案时再次将前缀和转化为单个的值(换来换去好乱啊……)。

    还有一点,时间复杂度不容乐观啊,转移的时候如果k循环到了 size[x],那么复杂度可以被卡到 N^3,我们发现当 k>size[v(j)]+1 的时候f[i]没有变,所以只要 k 循环到 size[v(j)]+1 就行了。
    每个节点只有在 dp 它父亲时会被枚举成为重儿子,然后最多把整棵树的大小扫一遍,所以复杂度为$n^2$(蒟弱表示并不会证).

    ps.有同学问我g数组的含义(好像是没写太清楚),g[k]=∑f[x][l](l<=k)即前缀和,可能用文字描述更好理解点,就是v(j)之前的儿子中最长轻链长度小于等于k的概率。

     1 LL f[3010][3010],g[3010],h[3010],size[3010];
     2 void dfs(int x)
     3 {
     4     size[x]=1;
     5     for(int i=f(x);i;i=n(i))dfs(v(i)),size[x]+=size[v(i)];
     6     LL q=poww(du[x],mod-2);
     7     for(int i=f(x);i;i=n(i))//枚举重儿子
     8     {    
     9         for(int j=0;j<=n;j++)g[j]=1;
    10         for(int j=f(x);j;j=n(j))
    11         {
    12             for(int k=0;k<=size[v(j)]+1;k++)
    13             {
    14                 LL gs=g[k];      if(k)gs-=g[k-1];
    15                 LL fs=f[v(j)][k];if(k)fs-=f[v(j)][k-1];
    16                 if(v(j)==v(i))h[k]= ((gs*f[v(j)][k]%mod+fs*g[k]%mod-gs*fs)%mod+mod)%mod;
    17                 else if(k)
    18                 {
    19                     fs=f[v(j)][k-1];if(k>1)fs-=f[v(j)][k-2];
    20                     h[k]=(  (gs*f[v(j)][k-1]%mod+fs*g[k]%mod-fs*gs%mod)%mod+mod  )%mod;
    21                 }
    22             }
    23             g[0]=h[0],h[0]=0;
    24             for(int k=1;k<=size[v(j)]+1;k++)g[k]=(g[k-1]+h[k])%mod,h[k]=0;
    25         }
    26         for(int j=size[x];j;j--)g[j]=(g[j]-g[j-1]+mod)%mod;
    27         for(int j=0;j<=size[x];j++)f[x][j]=(f[x][j]+g[j]*q%mod)%mod;
    28     }
    29     if(!f(x))f[x][0]=1;
    30     for(int i=1;i<=n;i++)f[x][i]=(f[x][i-1]+f[x][i])%mod;
    31 }
    #include<iostream>
    #include<cstdio>
    #include<cmath>
    #define LL long long
    #define int LL
    #define MAXN 50100
    #define esp 1e-8
    #define mod 1000000007
    using namespace std;
    struct edge
    {
    	int u,v,nxt;
    	#define u(x) ed[x].u
    	#define v(x) ed[x].v
    	#define n(x) ed[x].nxt
    }ed[5000000];
    int first[MAXN],num_e;
    #define f(x) first[x]
    int n,root;
    int du[MAXN],ru[MAXN];
    
    LL poww(LL a,int b);
    inline void add(int u,int v)
    {
    	++num_e;
    	u(num_e)=u;
    	v(num_e)=v;
    	n(num_e)=f(u);
    	f(u)=num_e;
    }
    inline int read()
    {
    	int s=0;char a=getchar();
    	while(a<'0'||a>'9')a=getchar();
    	while(a>='0'&&a<='9'){s=s*10+a-'0';a=getchar();}
    	return s;
    }
    LL f[3010][3010],g[3010],h[3010],size[3010];
    
    void dfs(int x)
    {
    	size[x]=1;
    	for(int i=f(x);i;i=n(i))dfs(v(i)),size[x]+=size[v(i)];
    //	cout<<x<<" "<<size[x]<<endl;
    	
    	LL q=poww(du[x],mod-2);
    	for(int i=f(x);i;i=n(i))//枚举重儿子
    	{	
    		for(int j=0;j<=n;j++)g[j]=1;
    		for(int j=f(x);j;j=n(j))
    		{
    			for(int k=0;k<=size[v(j)]+1;k++)
    			{
    				LL gs=g[k];      if(k)gs-=g[k-1];
    				LL fs=f[v(j)][k];if(k)fs-=f[v(j)][k-1];
    				if(v(j)==v(i))h[k]= ((gs*f[v(j)][k]%mod+fs*g[k]%mod-gs*fs)%mod+mod)%mod;
    				else if(k)
    				{
    					fs=f[v(j)][k-1];if(k>1)fs-=f[v(j)][k-2];
    					h[k]=(  (gs*f[v(j)][k-1]%mod+fs*g[k]%mod-fs*gs%mod)%mod+mod  )%mod;
    				}
    			}
    			g[0]=h[0],h[0]=0;
    			for(int k=1;k<=size[v(j)]+1;k++)g[k]=(g[k-1]+h[k])%mod,h[k]=0;
    		}
    		for(int j=size[x];j;j--)g[j]=(g[j]-g[j-1]+mod)%mod;
    		for(int j=0;j<=size[x];j++)f[x][j]=(f[x][j]+g[j]*q%mod)%mod;
    	}
    	if(!f(x))f[x][0]=1;
    	for(int i=1;i<=n;i++)f[x][i]=(f[x][i-1]+f[x][i])%mod;
    }
    signed main()
    {
    //	freopen("in.txt","r",stdin);
    
    	n=read();int a;
    //	for(int i=0;i<=n;i++)m[i]=1;
    	for(int i=1;i<=n;i++)		
    	{
    		du[i]=read();
    		for(int j=1;j<=du[i];j++)
    		a=read(),add(i,a),ru[a]++;
    	}
    	bool pdl=1;
    	for(int i=1;i<=n;i++)
    	{
    		if(du[i]!=1&&du[i]!=0)pdl=0;
    		if(!ru[i])root=i;
    	}
    	dfs(root);
    	LL ans=0;
    	for(int i=1;i<=n;i++)ans=(ans+i*(f[root][i]-f[root][i-1]+mod)%mod)%mod;
    	printf("%lld
    ",ans);
    }
    LL poww(LL a,int b)
    {
    	LL ans=1;
    	while(b)
    	{
    		if(b&1)ans=(ans*a)%mod;
    		a=(a*a)%mod;
    		b=b>>1;
    	}
    	return ans;
    }
    
  • 相关阅读:
    https://github.com/zyq025/DotNetCoreStudyDemo/blob/main/HttpClientConsoleDemo/Program.cs
    NETCORE TASK多线程的使用
    .NetCore简单封装基于IHttpClientFactory的HttpClient请求
    Netcore中简单使用MemoryCache
    LinQ To Object 基本用法
    SelectMany 和 Select的区别
    List 转换成字符串
    【转】C#中enum的总结
    发个服务端包裹返回值的Response类和用法
    微软企业库 Microsoft Enterprise Library
  • 原文地址:https://www.cnblogs.com/Al-Ca/p/11188037.html
Copyright © 2011-2022 走看看