「九省联考 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