【生成函数+拉格朗日插值+整体DP+线段树合并】LGP4365 [九省联考2018]秘密袭击coat

版权声明:这是蒟蒻的BLOG,神犇转载也要吱一声哦~ https://blog.csdn.net/Dream_Lolita/article/details/83351431

【题目】
原题地址
给定一棵 N N 个节点的树,点权 1 W 1 \sim W ,求树的每一个连通块的第 K K 大点权之和。

【解题思路】
以下均来自这里

首先可以转化一下题目。

A n s = S T k t h    o f    S = i = 1 W i × S T [ k t h   o f   S = = i ] = i = 1 W S T [ k t h   o f   S i ] = i = 1 W S T [ c n t [ i ] k ] \begin{aligned} Ans &= \sum_{S \in T} k_{th} \ \ of \ \ S \\ &= \sum_{i=1}^{W} i \times \sum_{S \in T} [k_{th}\ of \ S==i]\\ &=\sum_{i=1}^{W} \sum_{S \in T} {[k_{th} \ of \ S \geqslant i]}\\ &=\sum_{i=1}^{W} \sum_{S \in T} {[cnt[i] \geqslant k]} \end{aligned}
现在问题转化为了枚举一个权值 v v ,求 v v 的权值出现次数超过 k k 的连通块个数。
f [ i ] [ j ] [ k ] f[i][j][k] 表示以 i i 为根的子树,大于等于 j j 的权值出现次数大于等于 k k 的方案数。
f [ i ] [ j ] [ k ] = v s o n [ i ] f [ v ] [ j ] [ k ]      ( d [ i ] < j , k = k ) f [ i ] [ j ] [ k ] = v s o n [ i ] f [ v ] [ j ] [ k ]      ( d [ i ] j , k = k 1 ) \begin{aligned} f[i][j][k] &= \prod_{v \in son[i]} f[v][j][k'] \ \ \ \ (d[i]<j,\sum k'=k)\\ f[i][j][k] &= \prod_{v \in son[i]} f[v][j][k'] \ \ \ \ (d[i] \geqslant j,\sum k'=k-1) \end{aligned}
答案就是 k = 1 k j = 1 W i = 1 N f [ i ] [ j ] [ k ] \sum\limits_{k’=1}^{k}\sum\limits_{j=1}^{W}\sum\limits_{i=1}^{N} f[i][j][k’]

这个复杂度显然还不够优秀。
观察到转移实际上相当于一个背包,我们可以考虑生成函数直接算出系数。

F [ i ] [ j ] F[i][j] 表示以 i i 为根的子树中,权值大于等于 j j 的权值的生成函数。

F [ i ] [ j ] = k = 0 n f [ i ] [ j ] [ k ] × x k F[i][j]=\sum\limits_{k=0}^{n} f[i][j][k] \times x^k ,这是一个 N N 次多项式。

但是最后我们要求的是整棵树的所有 F [ i ] [ j ] F[i][j] 之和,所以我们不妨再设一个 G [ i ] [ j ] G[i][j]

G [ i ] [ j ] = x s u b t r e e ( i ) F [ x ] [ j ] G[i][j]=\sum\limits_{x \in subtree(i)}F[x][j]

F [ i ] [ j ] F[i][j] 在转移时是多项式卷积,还是很慢, G [ i ] [ j ] G[i][j] 在转移时只要维护一下就行了。

所以我们考虑将它转换为$N+1$1个点值,这样的话转移时就是普通乘法了。

我们就令 x = 1 N + 1 x=1 \sim N+1 ,然后将所有 G [ i ] [ j ] G[i][j] x x 时的值都求出来,最后进行拉格朗日插值法将原始的多项式差出来就行了,可具体怎么实现呢?

我们首先在最外层枚举 x [ 1 , N + 1 ] x \in [1,N+1] ,然后每次进行一次 D F S DFS ,但具体如何进行转移呢?

我们不难发现, F [ i ] [ j ] F [ s o n [ i ] ] [ j ] F[i][j] \leftarrow F[son[i]][j] 转移过程中其实就是 [ j ] [j] 的对应位置相乘。

所以我们可以使用整体 D P DP 的思想在每个点上都维护一颗线段树,然后在转移时进行线段树合并就可以了。

具体合并方法如下:

初始化: F [ i ] [ j ] = x      ( d [ i ] j ) F[i][j] = x \ \ \ \ (d[i] \geqslant j)

F [ i ] [ j ] = 1      ( d [ i ] < j ) F[i][j] = 1 \ \ \ \ (d[i] < j)

转移时: F [ i ] [ j ] × = ( F [ s o n [ i ] ] [ j ] + 1 ) , G [ i ] [ j ] + = G [ s o n [ i ] ] [ j ] F[i][j] \times =(F[son[i]][j]+1) , G[i][j]+=G[son[i]][j]

最后, G [ i ] [ j ] + = F [ i ] [ j ] G[i][j]+=F[i][j]

我们可以将 F [ s o n [ i ] ] [ j ] + 1 F[son[i]][j]+1 的操作放在 D F S DFS s o n [ i ] son[i] 后进行。

那么线段树到底应该怎么写?

我们设变换 ( a , b , c , d ) (a,b,c,d) 可以使 ( f , g ) (f,g) 变换为$(a \times f+b,c \times f+d+g)

然后维护它即可。

【参考代码】

#include<bits/stdc++.h>
using namespace std;

typedef long long LL;
const int N=2005,M=N*50,mod=64123;
int n,K,W,tot;
int d[N],head[N],inv[N],yc[N],c[N],g[N],f[N];

int read()
{
	int ret=0;char c=getchar();
	while(!isdigit(c)) c=getchar();
	while(isdigit(c)) ret=ret*10+(c^48),c=getchar();
	return ret;
}

inline void up(int &x,int y){x+=y;if(x>=mod)x-=mod;}
inline int upm(int x,int y){return x+y>=mod?x+y-mod:x+y;}
inline int mul(int x,int y){return (LL)x*y%mod;}
int qpow(int x,int y){int ret=1;for(;y;y>>=1,x=(LL)x*x%mod)if(y&1)ret=(LL)ret*x%mod;return ret;}

struct Tway{int v,nex;}e[N<<1];
void add(int u,int v)
{
	e[++tot]=(Tway){v,head[u]};head[u]=tot;
	e[++tot]=(Tway){u,head[v]};head[v]=tot;
}

struct data
{
	int a,b,c,d;
	data():a(1),b(0),c(0),d(0){}
	data(int a,int b,int c,int d):a(a),b(b),c(c),d(d){}	
	inline friend data operator * (const data&x,const data&y)
	{
		return data(mul(x.a,y.a),upm(mul(y.a,x.b),y.b),
			        upm(mul(y.c,x.a),x.c),upm(mul(y.c,x.b),upm(x.d,y.d)));
	}
};

struct Segment
{
	data tar[M];
	int top,sz;
	int str[M],rt[N],lc[M],rc[M];

	inline int newnode()
	{
		int x=top?str[top--]:++sz;
		tar[x]=data();lc[x]=rc[x]=0;
		return x;
	}
	
	inline void dele(int &x)
	{
		if(!x) return;
		dele(lc[x]);dele(rc[x]);
		str[++top]=x;x=0;
	}

	inline void pushdown(int x)
	{
		if(!lc[x]) lc[x]=newnode();
		if(!rc[x]) rc[x]=newnode();
		tar[lc[x]]=tar[lc[x]]*tar[x];
		tar[rc[x]]=tar[rc[x]]*tar[x];
		tar[x]=data();
	}

	inline void update(int &x,int l,int r,int L,int R,data v)
	{
		if(!x) x=newnode();
		if(L<=l && r<=R) {tar[x]=tar[x]*v;return;}
		pushdown(x);
		int mid=(l+r)>>1;
		if(L<=mid) update(lc[x],l,mid,L,R,v);
		if(R>mid) update(rc[x],mid+1,r,L,R,v);
	}

	void merge(int &x,int &y)
	{
		if(!x) swap(x,y); if(!y) return;
		if(!lc[x] && !rc[x]) swap(x,y);
		if(!lc[y] && !rc[y]) 
		{
			tar[x].a=mul(tar[x].a,tar[y].b);
			tar[x].b=mul(tar[x].b,tar[y].b);
			tar[x].d=upm(tar[x].d,tar[y].d);
			return;
		}
		pushdown(x);pushdown(y);
		merge(lc[x],lc[y]);merge(rc[x],rc[y]);
	}

	void init(int x,int fa,int x0)
	{
		update(rt[x],1,W,1,W,data(0,1,0,0));
		for(int i=head[x];i;i=e[i].nex)
		{
			int v=e[i].v;
			if(v==fa) continue;
			init(v,x,x0);merge(rt[x],rt[v]);dele(rt[v]);
		}
		if(d[x]) update(rt[x],1,W,1,d[x],data(x0,0,0,0));
		update(rt[x],1,W,1,W,data(1,0,1,0));
		update(rt[x],1,W,1,W,data(1,1,0,0));
	}

	void getval(int x,int l,int r,int p)
	{
		if(l==r){up(yc[p],tar[x].d);return;}
		pushdown(x);
		int mid=(l+r)>>1;
		getval(lc[x],l,mid,p);getval(rc[x],mid+1,r,p);
	}

	void div(int *a,int *b,int x0)
	{
		for(int i=0;i<=n+1;++i) c[i]=a[i];
		for(int i=n+1;i;--i)
			b[i-1]=c[i],up(c[i-1],mul(c[i],x0)),c[i]=0;
	}
}tr;

int calc()
{
	for(int i=1;i<=n+1;++i) inv[i]=qpow(i,mod-2); g[0]=1;
	for(int i=1;i<=n+1;++i) for(int j=n+1;~j;--j)
	{
		g[j]=mul(g[j],mod-i);
		if(j) up(g[j],g[j-1]);
	}

	int ans=0;
	for(int i=1;i<=n+1;++i)
	{
		tr.div(g,f,i);int res=0;
		for(int j=K;j<=n;++j) up(res,f[j]);
		for(int j=1;j<=n+1;++j) if(i^j)
			res=(i>j?mul(res,inv[i-j]):mul(res,mod-inv[j-i]));
		res=mul(res,yc[i]);up(ans,res);
	}
	return ans;
}

int main()
{
#ifndef ONLINE_JUDGE
	freopen("LGP4365.in","r",stdin);
	freopen("LGP4365.out","w",stdout);
#endif
	n=read();K=read();W=read();
	for(int i=1;i<=n;++i) d[i]=read();
	for(int i=1;i<n;++i) add(read(),read());
	for(int i=1;i<=n+1;++i)
		tr.init(1,0,i),tr.getval(tr.rt[1],1,W,i),tr.dele(tr.rt[1]);
	printf("%d\n",calc());
	return 0;
}

【总结】
说无可说。

猜你喜欢

转载自blog.csdn.net/Dream_Lolita/article/details/83351431