zoukankan      html  css  js  c++  java
  • 【PKUSC2019】线弦图【计数】【树形DP】【分治FFT】

    Description

    定义线图为把无向图的边变成点,新图中点与点之间右边当且仅当它们对应的边在原图中有公共点,这样得到的图。
    定义弦图为不存在一个长度大于3的纯环,纯环的定义是在环上任取两个不相邻的点,它们之间都没有边,也就是不存在没有弦的环的无向图。

    现在给出一棵n个点的树,你可以在上面添加任意多条边(不能重边),要求得到的图的线图是弦图,求加边的方案数。
    n<=200000

    Solution

    画图可以发现,一个无向图的线图是弦图的充要条件就是不存在长度大于3的环(不一定是纯环)

    也就是说,我们加边只能加成三角形,并且加的边还不能交叉。
    转化后的题意等价于将树上的边分成若干个集合,每个集合要么是单独一条边,要么是两条相邻的边,问方案数。

    (O(n^2))的暴力呼之欲出,我们记(f[i][0/1])表示处理完(i)的子树,(i)向父亲的这条边是否与子树内的边组合了。

    转移的时候,我们只需要知道所有儿子中有几个0,也就是有几条边需要我们来分配。

    不妨再DP预处理出一个数组(g[i][0/1]),表示一个点下面挂着i条边,要分配集合,i的父亲边是否参与的方案数。

    我们考虑每次新加一条边,要么自成集合,要么与之前的某一个组合,有
    (g[i][0]=g[i-1]+g[i-2][0]*(i-1),g[i][1]=g[i-1][0]*i)

    由于只要知道有几个0,这显然可以分治NTT优化,这样就做完了。
    时间复杂度(O(nlog^2 n))

    Code

    以下代码未经测试,完全不能保证正确性。
    (惨遭证伪,请勿参考)

    #include <bits/stdc++.h>
    #define fo(i,a,b) for(int i=a;i<=b;++i)
    #define fod(i,a,b) for(int i=a;i>=b;--i)
    #define N 200005
    #define M 524288
    using namespace std;
    const int mo=998244353;
    typedef long long LL;
    int n,m1,fs[N],nt[2*N],dt[2*N];
    LL f[N][2],g[N];
    LL ksm(LL k,LL n)
    {
    	LL s=1;
    	for(;n;n>>=1,k=k*k%mo) if(n&1) s=s*k%mo;
    	return s;
    }
    void link(int x,int y)
    {
    	nt[++m1]=fs[x];
    	dt[fs[x]=m1]=y;
    }
    namespace poly
    {
    	int len,st[N],le[N],a[M+1];
    	int u1[M+1],u2[M+1],l2[M+1],cf[20],wg[M+1],wi[M+1],ny[M+1],bit[M+1];
    	void init()
    	{
    		fo(i,0,18) l2[cf[i]=(1<<i)]=i;
    		fod(i,M-1,2) if(!l2[i]) l2[i]=l2[i+1];
    		wg[0]=1,wg[1]=ksm(3,(mo-1)/M);
    		ny[1]=1;
    		fo(i,2,M) wg[i]=(LL)wg[i-1]*wg[1]%mo,ny[i]=(-(LL)ny[mo%i]*(mo/i)%mo+mo)%mo;
    	}
    	void prp(int num)
    	{
    		int p=M/num,w=cf[l2[num]-1];
    		fo(i,0,num-1) bit[i]=(bit[i>>1]>>1)|((i&1)<<w),wi[i]=wg[i*p];
    	}
    	int inc(int x,int y) {x+=y;return(x>=mo)?x-mo:x;}
    	int dec(int x,int y) {x-=y;return(x<0)?x+mo:x;}
    
    	void DFT(int *a,int num)
    	{
    		fo(i,0,num-1) if(i<bit[i]) swap(a[i],a[bit[i]]);
    		for(int h=1,l=num>>1;h<num;h<<=1,l>>=1)
    		{
    			for(int j=0;j<num;j+=(h<<1))
    			{
    				int *x=a+j,*y=x+h,*w=wi,v;
    				for(int i=0;i<h;i++,x++,y++,w+=l)
    				{
    					v=(LL)(*x)*(*y)%mo;
    					*y=dec(*x,v),*x=inc(*x,v);
    				}
    			}
    		}
    	}
    	void IDFT(int *a,int num)
    	{
    		DFT(a,num);
    		fo(i,0,num-1) a[i]=(LL)a[i]*ny[num]%mo;
    		reverse(a+1,a+num);
    	}
    
    	void doit(int l,int r)
    	{
    		if(l==r) return; 
    		int mid=(l+r)>>1;
    		doit(l,mid),doit(mid+1,r);
    		int num=cf[l2[le[l]+le[mid+1]-1]];
    		prp(num);
    		memset(u1,0,4*num),memset(u2,0,4*num);
    		fo(i,0,le[l]-1) u1[i]=a[st[l]+i];
    		fo(i,0,le[mid+1]-1) u2[i]=a[st[mid]+i];
    		DFT(u1,num),DFT(u2,num);
    		fo(i,0,num-1) u1[i]=(LL)u1[i]*u2[i]%mo;
    		IDFT(u1,num);
    		le[l]+=le[mid+1]-1;
    		fo(i,0,le[l]-1) a[st[l]+i]=u1[i];
    	}
    }
    using namespace poly;
    
    
    void dfs(int k,int fa)
    {
    	for(int i=fs[k];i;i=nt[i]) 
    	{
    		int p=dt[i];
    		if(p!=fa) dfs(p,k);
    	}
    	len=0;
    	int cnt=0;
    	for(int i=fs[k];i;i=nt[i])
    	{
    		int p=dt[i];
    		if(p!=fa)
    		{
    			cnt++;
    			a[st[cnt]=len++]=f[p][1];
    			a[len++]=f[p][0];
    			le[cnt]=2;
    		}
    	}
    	if(!cnt) f[k][0]=1;
    	else
    	{
    		doit(1,cnt);
    		f[k][0]=f[k][1]=0;
    		fo(i,0,le[1]-1)
    		{
    			f[k][0]=(f[k][0]+(LL)a[i]*g[i])%mo;
    			if(i) f[k][1]=(f[k][1]+(LL)a[i]*g[i-1]%mo*(LL)i)%mo;
    		}
    	}
    }
    int main()
    {
    	cin>>n;
    	fo(i,1,n-1)
    	{
    		int x,y;
    		scanf("%d%d",&x,&y);
    		link(x,y),link(y,x);
    	}
    	g[0]=1,g[1]=1;
    	fo(i,2,n) g[i]=(g[i-1]+g[i-2]*(i-1))%mo;
    	init();
    	dfs(1,0);
    	printf("%lld
    ",f[1][0]);
    }
    
  • 相关阅读:
    IPC之msgutil.c源码解读
    IPC之msg.c源码解读
    IPC之mqueue.c源码解读
    从锅炉工到AI专家(10)
    从锅炉工到AI专家(9)
    从锅炉工到AI专家(8)
    从锅炉工到AI专家(7)
    从锅炉工到AI专家(6)
    从锅炉工到AI专家(5)
    从锅炉工到AI专家(4)
  • 原文地址:https://www.cnblogs.com/BAJimH/p/10945891.html
Copyright © 2011-2022 走看看