暴力乱艹题,然而我模拟赛被卡80,LOJ上被卡95(其实再优化一下上界就可以了但太懒了)
首先我们考虑转化题意((T)表示树上的一个联通块集合,(F(S,i))表示联通块(S)中权值(ge i)的点的个数):
[ans=sum_{Sin T} kth of S\=sum_{i=1}^w isum_{Sin T} [kth of S=i]\=sum_{i=1}^w sum_{Sin T} [kth of Sge i]\=sum_{i=1}^w sum_{Sin T} [F(S,i)ge k]
]
因此我们考虑每次枚举一个权值(v),设计状态(f_{x,y})表示在(x)子树内且强制选(x)且恰好有(y)个权值(ge v)的点的联通块数目
转移的时候显然就是一个类树上背包,算上外层的枚举复杂度是(O(n^2 imes w))的
容易发现这个算法常数很小且跑不满,因此可以通过此题
PS:正解是线段树打标记维护点值,根本看不懂的说(然后因为常数被暴力吊打)
#pragma GCC optimize(2)
#include<cstdio>
#include<cstring>
#define RI register int
#define CI const int&
using namespace std;
const int N=2005,mod=64123;
struct edge
{
int to,nxt;
}e[N<<1]; int n,m,w,x,y,head[N],cnt,size[N],suf[N],a[N]; long long f[N][N],ans;
inline void addedge(CI x,CI y)
{
e[++cnt]=(edge){y,head[x]}; head[x]=cnt;
e[++cnt]=(edge){x,head[y]}; head[y]=cnt;
}
#define to e[i].to
inline void DP(CI lim,CI now=1,CI fa=0)
{
RI i,j,k; long long *F=f[now]; F[size[now]=a[now]>=lim]=1;
for (i=head[now];i;i=e[i].nxt) if (to!=fa)
{
for (DP(lim,to,now),j=size[now];~j;--j) if (F[j])
for (k=size[to];~k;--k) F[j+k]+=F[j]*f[to][k];
for (size[now]+=size[to],j=size[now];~j;--j) F[j]%=mod;
}
for (i=m;i<=size[now];++i) ans+=f[now][i];
}
#undef to
int main()
{
//freopen("num.in","r",stdin); freopen("num.out","w",stdout);
RI i; for (scanf("%d%d%d",&n,&m,&w),i=1;i<=n;++i) scanf("%d",&a[i]),++suf[a[i]];
for (i=1;i<n;++i) scanf("%d%d",&x,&y),addedge(x,y);
for (i=w-1;i;--i) suf[i]+=suf[i+1]; for (i=1;i<=w;++i)
if (suf[i]>=m) memset(f,0,sizeof(f)),DP(i); return printf("%d",ans%mod),0;
}