【生成函式+拉格朗日插值+整體DP+線段樹合併】LGP4365 [九省聯考2018]祕密襲擊coat
阿新 • • 發佈:2019-02-13
【題目】
原題地址
給定一棵個節點的樹,點權,求樹的每一個連通塊的第大點權之和。
【解題思路】
以下均來自這裡
首先可以轉化一下題目。
現在問題轉化為了列舉一個權值,求的權值出現次數超過的連通塊個數。
設表示以為根的子樹,大於等於的權值出現次數大於等於的方案數。
答案就是
這個複雜度顯然還不夠優秀。
觀察到轉移實際上相當於一個揹包,我們可以考慮生成函式直接算出係數。
設表示以為根的子樹中,權值大於等於的權值的生成函式。
則,這是一個次多項式。
但是最後我們要求的是整棵樹的所有之和,所以我們不妨再設一個。
在轉移時是多項式卷積,還是很慢,在轉移時只要維護一下就行了。
所以我們考慮將它轉換為$N+1$1個點值,這樣的話轉移時就是普通乘法了。
我們就令,然後將所有在時的值都求出來,最後進行拉格朗日插值法將原始的多項式差出來就行了,可具體怎麼實現呢?
我們首先在最外層列舉,然後每次進行一次,但具體如何進行轉移呢?
我們不難發現,轉移過程中其實就是的對應位置相乘。
所以我們可以使用整體的思想在每個點上都維護一顆線段樹,然後在轉移時進行線段樹合併就可以了。
具體合併方法如下:
初始化:
轉移時:
最後,。
我們可以將的操作放在 後進行。
那麼線段樹到底應該怎麼寫?
我們設變換可以使變換為$(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;
}
【總結】
說無可說。