zoukankan      html  css  js  c++  java
  • CF1010F Tree

    Tree

    一颗二叉树,根节点为 (1),允许你砍掉任意子树,保留根节点所在的连通块,然后要求你设置每个点的权值,要求每个点权值不小于它的两个儿子的权值之和,且根节点权值为 (x) 的方案数,连通块不一样或者点的权值不一样都算不同方案。

    (1≤n≤10^5 , 0≤x≤10^{18})

    题解

    https://blog.csdn.net/zxyoi_dreamer/article/details/101623399

    首先第一步转化,题目要求每个点的权值不小于儿子的权值之和,转换一下每个点减去两个儿子的权值,实际上是这样一个问题:给每个点赋一个非负权值,且权值和为(x)(即原题中根节点的限制),发现这个和树的形态没有关系只和树的siz有关系,插板法可以算。

    那么现在问题就是树上选一个包含根节点的联通块,大小为(k)的方案数。

    树形背包可以(O(n^2))算,但是注意到可以转化成生成函数的形式,设(F_u(x))表示以(u)为根,选择大小为(k~(k>0))的联通块的方案数。

    轻重链剖分之后对于每条重链分治NTT算出链顶的答案即可。

    时间复杂度 (O(n log^3 n))

    细节:https://blog.csdn.net/sslz_fsy/article/details/103358952

    CO int N=262144;
    int omg[2][N],rev[N];
    int fac[N],inv[N],ifac[N];
    
    void NTT(poly&a,int dir){
    	int lim=a.size(),len=log2(lim);
    	for(int i=0;i<lim;++i) rev[i]=rev[i>>1]>>1|(i&1)<<(len-1);
    	for(int i=0;i<lim;++i)if(i<rev[i]) swap(a[i],a[rev[i]]);
    	for(int i=1;i<lim;i<<=1)
    		for(int j=0;j<lim;j+=i<<1)for(int k=0;k<i;++k){
    			int t=mul(omg[dir][N/(i<<1)*k],a[j+i+k]);
    			a[j+i+k]=add(a[j+k],mod-t),a[j+k]=add(a[j+k],t);
    		}
    	if(dir==1){
    		int ilim=fpow(lim,mod-2);
    		for(int i=0;i<lim;++i) a[i]=mul(a[i],ilim);
    	}
    }
    poly operator*(poly a,poly b){
    	int n=a.size()+b.size()-1,lim=1<<(int)ceil(log2(n));
    	a.resize(lim),NTT(a,0);
    	b.resize(lim),NTT(b,0);
    	for(int i=0;i<lim;++i) a[i]=mul(a[i],b[i]);
    	NTT(a,1),a.resize(n);
    	return a;
    }
    poly operator+(CO poly&a,CO poly&b){
    	poly ans=a;
    	if(a.size()<b.size()) ans.resize(b.size());
    	for(int i=0;i<(int)b.size();++i) ans[i]=add(ans[i],b[i]);
    	return ans;
    }
    
    vector<int> to[N];
    int fa[N],siz[N],son[N];
    
    void dfs(int u,int fa){
    	::fa[u]=fa,siz[u]=1;
    	for(int v:to[u])if(v!=fa){
    		dfs(v,u);
    		siz[u]+=siz[v];
    		if(siz[v]>siz[son[u]]) son[u]=v;
    	}
    }
    
    poly a[N],p[N];
    
    pair<poly,poly> solve(int l,int r){ // all,ans
    	if(l==r) return {a[l],a[l]};
    	int mid=(l+r)>>1;
    	pair<poly,poly> lans=solve(l,mid);
    	pair<poly,poly> rans=solve(mid+1,r);
    	return {lans.first*rans.first,lans.second+lans.first*rans.second};
    }
    poly divide(int u){
    	for(int i=u;i;i=son[i]){
    		for(int v:to[i])if(v!=fa[i] and v!=son[i]){
    			p[i]=divide(v);break; // binary tree
    		}
    		if(p[i].empty()) p[i].push_back(0);
    		p[i][0]=1,p[i].insert(p[i].begin(),0);
    	}
    	int l=0;
    	for(int i=u;i;i=son[i]) a[++l].swap(p[i]);
    	return solve(1,l).second;
    }
    
    int main(){
    	omg[0][0]=1,omg[0][1]=fpow(3,(mod-1)/N);
    	omg[1][0]=1,omg[1][1]=fpow(omg[0][1],mod-2);
    	fac[0]=fac[1]=1;
    	inv[0]=inv[1]=1;
    	ifac[0]=ifac[1]=1;
    	for(int i=2;i<N;++i){
    		omg[0][i]=mul(omg[0][i-1],omg[0][1]);
    		omg[1][i]=mul(omg[1][i-1],omg[1][1]);
    		fac[i]=mul(fac[i-1],i);
    		inv[i]=mul(mod-mod/i,inv[mod%i]);
    		ifac[i]=mul(ifac[i-1],inv[i]);
    	}
    	int n=read<int>(),X=read<int64>()%mod;
    	for(int i=1;i<n;++i){
    		int u=read<int>(),v=read<int>();
    		to[u].push_back(v),to[v].push_back(u);
    	}
    	dfs(1,0);
    	poly cnt=divide(1);
    	int ans=0,binom=1;
    	for(int i=1;i<(int)cnt.size() and binom;++i){
    		ans=add(ans,mul(binom,cnt[i]));
    		binom=mul(binom,mul(add(X,i),inv[i]));
    	}
    	printf("%d
    ",ans);
    	return 0;
    }
    
  • 相关阅读:
    WebBrowser与console.log()
    winform如何让窗体不显示Icon但在任务栏中显示Icon
    (asp.net)百度浏览器Cookie的神奇bug
    winform无边框窗体点击任务栏最小化
    Paper Reading_Distributed System
    Paper Reading_System for ML
    Paper Reading_Database and Storage System
    Paper Reading_ML for system
    Paper Reading_Computer Architecture
    两万多字长文:人工智能新趋势(CB Insights)
  • 原文地址:https://www.cnblogs.com/autoint/p/12329967.html
Copyright © 2011-2022 走看看