LOJ #2473. 「九省联考 2018」秘密袭击

暴力乱艹题,然而我模拟赛被卡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;
}
原文地址:https://www.cnblogs.com/cjjsb/p/13368896.html