zoukankan      html  css  js  c++  java
  • 6512. 【GDOI2020模拟3.18】树与路径

    题目描述


    题解

    我太难了

    见到树+dp+1e5直接刚dp启发式合并

    2h中经历了nlogn->n^2->n^3

    思想江化


    考虑把每个点相连的边配对,每配一次就代表把这两段拼起来

    n条边配m对的方案为C(n,2)*C(n-2,2)*...*C(n-2(m-1),2)/m!

    按哈夫曼树(合并果子)顺序合并多项式,时间O(n log^2 n)

    证明(大概):

    每次找长度最小的两个合并,时间为两个的长度(不考虑NTT的log)

    设当前剩余k段,则两个长度之和不超2n/(k-1)

    若超过,则剩余(k-2)段中最小的不超(k-3)n/((k-1)(k-2))

    而两端中较大者不小于n/(k-1),因此可以用剩余最小的把两段中较大者换掉

    那么k取值2~n时,长度和约为n ln n,大概就是log级别

    code

    #include <bits/stdc++.h>
    #define fo(a,b,c) for (a=b; a<=c; a++)
    #define fd(a,b,c) for (a=b; a>=c; a--)
    #define ll long long
    #define mod 998244353
    #define Mod 998244351
    #define G 3
    #define file
    using namespace std;
    
    struct type{
    	int x,y;
    	friend bool operator < (type a,type b) {return a.y>b.y;}
    };
    ll a[131072],b[131072],A[131072],jc[100001],Jc[100001],w[100001],s;
    int d[100001],n,i,j,k,l,N,len,x,y,n1,n2;
    vector<int> f[100001];
    priority_queue<type> heap;
    
    ll C(int n,int m)
    {
    	return jc[n]*Jc[m]%mod*Jc[n-m]%mod;
    }
    
    ll qpower(ll a,int b)
    {
    	ll ans=1;
    	
    	while (b)
    	{
    		if (b&1)
    		ans=ans*a%mod;
    		a=a*a%mod;
    		b>>=1;
    	}
    	
    	return ans;
    }
    
    void dft(ll *a,int type)
    {
    	int i,j,k,l,s1=2,s2=1,S=N;
    	ll w,W,u,v;
    	
    	fo(i,0,N-1)
    	{
    		j=i;k=0;
    		fo(l,1,len)
    		k=k*2+(j&1),j>>=1;
    		
    		A[k]=a[i];
    	}
    	memcpy(a,A,8*N);
    	
    	fo(i,1,len)
    	{
    		if (type==1)
    		w=qpower(G,(mod-1)/s1);
    		else
    		w=qpower(G,(mod-1)-(mod-1)/s1);
    		S>>=1;
    		
    		fo(j,0,S-1)
    		{
    			W=1;
    			
    			fo(k,0,s2-1)
    			{
    				u=a[j*s1+k];
    				v=a[j*s1+k+s2]*W;
    				
    				a[j*s1+k]=(u+v)%mod;
    				a[j*s1+k+s2]=(u-v)%mod;
    				W=W*w%mod;
    			}
    		}
    		
    		s1<<=1;s2<<=1;
    	}
    }
    
    int main()
    {
    	freopen("path.in","r",stdin);
    	#ifdef file
    	freopen("path.out","w",stdout);
    	#endif
    	
    	scanf("%d",&n);
    	w[1]=jc[0]=jc[1]=Jc[0]=Jc[1]=1;
    	fo(i,2,n)
    	{
    		w[i]=mod-w[mod%i]*(mod/i)%mod;
    		
    		jc[i]=jc[i-1]*i%mod;
    		Jc[i]=Jc[i-1]*w[i]%mod;
    		
    		scanf("%d%d",&j,&k);
    		++d[j];++d[k];
    	}
    	fo(i,1,n)
    	{
    		heap.push({i,d[i]/2});
    		
    		f[i].push_back(1);
    		s=1;
    		fo(j,1,d[i]/2)
    		{
    			s=s*C(d[i]-(j-1)*2,2)%mod;
    			f[i].push_back(s*Jc[j]%mod);
    		}
    		d[i]/=2;
    	}
    	
    	fo(i,1,n-1)
    	{
    		x=(heap.top()).x;heap.pop();
    		y=(heap.top()).x;heap.pop();
    		
    		n1=d[x]; fo(j,0,n1) a[j]=f[x][j]; f[x].clear();
    		n2=d[y]; fo(j,0,n2) b[j]=f[y][j]; f[y].clear();
    		
    		len=ceil(log2(n1+n2+1));N=qpower(2,len);
    		fo(j,n1+1,N-1) a[j]=0;
    		fo(j,n2+1,N-1) b[j]=0;
    		
    		dft(a,1);
    		dft(b,1);
    		fo(j,0,N-1) a[j]=a[j]*b[j]%mod;
    		dft(a,-1);
    		
    		N=qpower(N,Mod);
    		d[x]+=d[y];
    		fo(j,0,d[x])
    		f[x].push_back(a[j]*N%mod);
    		
    		heap.push({x,d[x]});
    	}
    	
    	x=(heap.top()).x;
    	fo(i,1,n-1)
    	if (i<(n-1)-d[x])
    	printf("0 ");
    	else
    	printf("%d ",(f[x][(n-1)-i]+mod)%mod);
    	printf("
    ");
    	
    	fclose(stdin);
    	fclose(stdout);
    	
    	return 0;
    }
    
  • 相关阅读:
    Java设计模式-装饰器模式
    【c++内存分布系列】单独一个类
    【转】LCS
    快速排序
    冒泡排序
    选择排序
    多线程读取全局变量
    【转】一致性hash算法(consistent hashing)
    【转】五笔的字典序编码与解码
    给定一个函数rand()能产生0到n-1之间的等概率随机数,问如何产生0到m-1之间等概率的随机数?
  • 原文地址:https://www.cnblogs.com/gmh77/p/12521561.html
Copyright © 2011-2022 走看看