1. 程式人生 > >【生成函式+拉格朗日插值+整體DP+線段樹合併】LGP4365 [九省聯考2018]祕密襲擊coat

【生成函式+拉格朗日插值+整體DP+線段樹合併】LGP4365 [九省聯考2018]祕密襲擊coat

【題目】
原題地址
給定一棵NN個節點的樹,點權1W1 \sim W,求樹的每一個連通塊的第KK大點權之和。

【解題思路】
以下均來自這裡

首先可以轉化一下題目。

Ans=STkthofS=i=1Wi×ST[kthofS==i]=i=1WST[kthofSi]=i=1WST[cnt[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}


現在問題轉化為了列舉一個權值vv,求vv的權值出現次數超過kk的連通塊個數。
f[i][j][k]f[i][j][k]表示以ii為根的子樹,大於等於jj的權值出現次數大於等於kk的方案數。
f[i][j][k]=vson[i]f[v][j][k](d[i]<j,k=k)f[i][j][k]=vson[i]f[v][j][k](d[i]j,k=k1
)\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=1kj=1Wi=1Nf[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]表示以ii為根的子樹中,權值大於等於jj的權值的生成函式。

F[i][j]=k=0nf[i][j][k]×xkF[i][j]=\sum\limits_{k=0}^{n} f[i][j][k] \times x^k,這是一個NN次多項式。

但是最後我們要求的是整棵樹的所有F[i][j]F[i][j]之和,所以我們不妨再設一個G[i][j]G[i][j]

G[i][j]=xsubtree(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=1N+1x=1 \sim N+1,然後將所有G[i][j]G[i][j]xx時的值都求出來,最後進行拉格朗日插值法將原始的多項式差出來就行了,可具體怎麼實現呢?

我們首先在最外層列舉x[1,N+1]x \in [1,N+1],然後每次進行一次DFSDFS,但具體如何進行轉移呢?

我們不難發現,F[i][j]F[son[i]][j]F[i][j] \leftarrow F[son[i]][j]轉移過程中其實就是[j][j]的對應位置相乘。

所以我們可以使用整體DPDP的思想在每個點上都維護一顆線段樹,然後在轉移時進行線段樹合併就可以了。

具體合併方法如下:

初始化: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[son[i]][j]+1),G[i][j]+=G[son[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[son[i]][j]+1F[son[i]][j]+1的操作放在DFSDFS son[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;
}

【總結】
說無可說。