「九省联考 2018」秘密袭击 解题报告

「九省联考 2018」秘密袭击

当然是选择\(n^3\)卡常数啊

首先可以钦定一个点或者钦定一个权值作为答案,也就是第\(k\)值,剩下的点权值就只有\(0\)或者\(1\)了,于是可以压到背包里面,做树上背包是\(O(n^2)\)

这里需要想一想如何处理相同权值的元素,我的方法是钦定每一个点,然后点权是第一关键字,点的标号是第二关键字,这样就没有相同元素了。

剪枝&卡常:

背包大小是子树\(1\)权值的个数和\(k\)\(\min\),如果整棵树的1的个数都没有k,就别做了

开ll,可以少取膜

然后就跑很快了


Code:

#include <cstdio>
#include <cstring>
#include <cctype>
#define ll long long
const int N=1667;
const int mod=64123;
inline void add(int &x,int y){x=x+y>=mod?x+y-mod:x+y;}
int min(int x,int y){return x<y?x:y;}
template <class T>
void read(T &x)
{
	x=0;char c=getchar();
	while(!isdigit(c)) c=getchar();
	while(isdigit(c)) x=x*10+c-'0',c=getchar();
}
int head[N],to[N<<1],Next[N<<1],cnt;
void addedge(int u,int v)
{
	to[++cnt]=v,Next[cnt]=head[u],head[u]=cnt;
}
ll dp[N][N],tmp[N];
int siz[N],cost[N],d[N],n,k,w,ans;
void dfs(int now,int fa,int rt)
{
	siz[now]=d[now];
	dp[now][d[now]]=1;
	for(int v,i=head[now];i;i=Next[i])
		if((v=to[i])!=fa)
		{
			dfs(v,now,rt);
			for(int j=0;j<=siz[now]+siz[v];j++) tmp[j]=0;
			for(int j=0;j<=siz[v];j++)
				for(int l=d[now];l<=siz[now];l++)
					tmp[j+l]+=dp[now][l]*dp[v][j];
			siz[now]+=siz[v];
			siz[now]=min(siz[now],k-1);
			for(int j=0;j<=siz[now];j++) (dp[now][j]+=tmp[j])%=mod;
		}
}
int main()
{
	read(n),read(k),read(w);
	for(int i=1;i<=n;i++) read(cost[i]);
	for(int v,u,i=1;i<n;i++) read(u),read(v),addedge(u,v),addedge(v,u);
	for(int i=1;i<=n;i++)
	{
	    int sum=0;
		for(int j=1;j<=n;j++)
			d[j]=(cost[i]<cost[j])||(cost[i]==cost[j]&&i<j),sum+=d[j];
        if(sum<k-1) continue;
		dfs(i,0,i);
		add(ans,cost[i]*dp[i][k-1]%mod);
		for(int j=1;j<=n;j++)
            for(int l=0;l<=siz[j];l++)
                dp[j][l]=0;
	}
	printf("%d\n",ans);
	return 0;
}

2019.3.18

posted @ 2019-03-18 10:57  露迭月  阅读(273)  评论(0编辑  收藏  举报