LOJ #2473. 「九省联考 2018」秘密袭击
暴力乱艹题,然而我模拟赛被卡80,LOJ上被卡95(其实再优化一下上界就可以了但太懒了)
首先我们考虑转化题意(\(T\)表示树上的一个联通块集合,\(F(S,i)\)表示联通块\(S\)中权值\(\ge i\)的点的个数):
\[ans=\sum_{S\in T} kth\ of\ S\\=\sum_{i=1}^w i\sum_{S\in T} [kth\ of\ S=i]\\=\sum_{i=1}^w \sum_{S\in T} [kth\ of\ S\ge i]\\=\sum_{i=1}^w \sum_{S\in T} [F(S,i)\ge k]
\]
因此我们考虑每次枚举一个权值\(v\),设计状态\(f_{x,y}\)表示在\(x\)子树内且强制选\(x\)且恰好有\(y\)个权值\(\ge v\)的点的联通块数目
转移的时候显然就是一个类树上背包,算上外层的枚举复杂度是\(O(n^2\times 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;
}
辣鸡老年选手AFO在即