【题目】
原题地址
给定一棵
个节点的树,点权
,求树的每一个连通块的第
大点权之和。
【解题思路】
以下均来自这里
首先可以转化一下题目。
现在问题转化为了枚举一个权值
,求
的权值出现次数超过
的连通块个数。
设
表示以
为根的子树,大于等于
的权值出现次数大于等于
的方案数。
答案就是
这个复杂度显然还不够优秀。
观察到转移实际上相当于一个背包,我们可以考虑生成函数直接算出系数。
设 表示以 为根的子树中,权值大于等于 的权值的生成函数。
则 ,这是一个 次多项式。
但是最后我们要求的是整棵树的所有 之和,所以我们不妨再设一个 。
在转移时是多项式卷积,还是很慢, 在转移时只要维护一下就行了。
所以我们考虑将它转换为$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;
}
【总结】
说无可说。