bzoj 1812
什么鬼noip互测题...
这题很显然是树形dp,但设计状态以及转移是个难点
记状态f[i][j][k]表示以i为根节点的子树,离i最近的祖宗节点编号为j放了虫洞(伐木场?),i的子树内放了k个伐木场的方案数
设to为i的某个子节点,当i不放伐木场时,有:
dp[i][j][k]=min(dp[to][j][k-c]+dp[i][j][c])
当i放伐木场时,有:
dp[i][i][k]=min(dp[to][i][k-c]+dp[i][i]c])
最后合并:
dp[i][j][k]+=num[i]*dis[i][j]
dp[i][j][k]=min(dp[i][j][k],dp[i][i][k])
其中dis[i][j]表示从i到j(j为i的某个祖先节点)的距离,可以预处理出来
特别的,当i为叶节点(即i没有子节点)时,dp[i][j][0]=dis[i][j]*num[i]直接赋值
#include <cstdio> #include <cmath> #include <cstring> #include <cstdlib> #include <iostream> #include <algorithm> #include <queue> #include <stack> #define ll long long using namespace std; int dp[205][205][55]; struct Edge { int next; int to; }edge[205]; int head[205]; int dis[205][205]; int num[205]; int f[205]; int n,k; int cnt=1; void init() { memset(head,-1,sizeof(head)); memset(f,-1,sizeof(f)); cnt=1; } void add(int l,int r) { edge[cnt].next=head[l]; edge[cnt].to=r; head[l]=cnt++; } void initdfs(int x) { if(x) { for(int i=f[x];i!=-1;i=f[i]) { dis[x][i]=dis[x][f[x]]+dis[f[x]][i]; } dis[x][0]=dis[x][f[x]]+dis[f[x]][0]; } for(int i=head[x];i!=-1;i=edge[i].next) { int to=edge[i].to; initdfs(to); } } void dfs(int x) { if(head[x]==-1) { for(int i=f[x];i!=-1;i=f[i]) { dp[x][i][0]=dis[x][i]*num[x]; } return; } if(x) { dp[x][x][0]=0x3f3f3f3f; } for(int i=head[x];i!=-1;i=edge[i].next) { int to=edge[i].to; dfs(to); for(int j=f[x];j!=-1;j=f[j]) { for(int t=k;t>=0;t--) { int temp=0x3f3f3f3f; for(int c=0;c<=t;c++) { temp=min(temp,dp[x][j][c]+dp[to][j][t-c]); } dp[x][j][t]=temp; } } for(int j=k;j>=0;j--) { int temp=0x3f3f3f3f; for(int c=(x!=0);c<=j;c++) { temp=min(temp,dp[x][x][c]+dp[to][x][j-c]); } dp[x][x][j]=temp; } } for(int i=f[x];i!=-1;i=f[i]) { for(int j=0;j<=k;j++) { dp[x][i][j]+=dis[x][i]*num[x]; dp[x][i][j]=min(dp[x][i][j],dp[x][x][j]); } } } inline int read() { int f=1,x=0;char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();} while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();} return x*f; } int main() { // freopen("girls.in","r",stdin); // freopen("girls.out","w",stdout); n=read(),k=read(); init(); f[0]=-1; for(int i=1;i<=n;i++) { int v; num[i]=read(),f[i]=read(),v=read(); dis[i][f[i]]=v; add(f[i],i); } initdfs(0); dfs(0); printf("%d\n",dp[0][0][k]); return 0; }