【JZOJ 5496】 Tree

Description

这里写图片描述
n<=3000

O(n^3)

可以发现的结论:最优策略下,贡献形如选的k个点形成的虚树的边权和*2-选的k个点的直径
可以枚举直径,再树形背包,应该能弄到O(n^3)

O(n^2)

从直径入手
H[x][k] 表示 x 的子树内选择了 k 个点, 并且这 k 个点的直径两端都不是 x , 构成的虚树总边长的最小值 ×2− 直径的长度的最小值。
有了这个便能求出答案了,但是要怎么算这个呢?
思考一下,发现需要两个辅助数组F,G
G[x][k] 表示 x 的子树内选择了 k 个点, 并且这 k 个点包含了 x, 构成的虚树总边长的最小值。
F[x][k] 表示 x 的子树内选择了 k 个点, 并且这 k 个点的直径有一端是 x, 构成的虚树总边长的最小值 ×2− 直径的长度的最小值。
然后就在树上DP就可以了
这样子转移时的复杂度看起来是O(n^3)的,但实际上由于每对点只会在它们的LCA处被算一次,所以总的复杂度是O(n^2)的

Codes

#include<cstdio>
#include<cstring>
#include<algorithm>
#define fo(i,a,b) for(int i=a;i<=b;++i)
#define fd(i,b,a) for(int i=b;i>=a;--i)
#define efo(i,v,u) for(int i=BB[v],u=B[BB[v]][1];i;i=B[i][0],u=B[i][1])
#define max(x,y) ((x)>(y)?(x):(y))
#define min(x,y) ((x)<(y)?(x):(y))
#define mset(a,x) memset(a,x,sizeof(a))
using namespace std;
typedef long long ll;
typedef double db;
char ch;
void read(int &n){n=0;int p=1;for(ch=getchar();ch<'0' || ch>'9';ch=getchar())if(ch=='-') p=-1;for(;'0'<=ch && ch<='9';ch=getchar()) n=n*10+ch-'0';n*=p;}
const int N=3005,INF=1e9;
int n,m,B0,B[N+N][3],BB[N];
void link(int u,int v,int w){B[++B0][1]=v,B[B0][0]=BB[u],B[B0][2]=w,BB[u]=B0;}
int ans,f[N][N],g[N][N],h[N][N];
void upd(int &x,int y){x=min(x,y);}
int dfs(int v,int fr=0)
{
    f[v][1]=g[v][1]=0;
    int sz=1;
    efo(i,v,u) if(u!=fr)
    {
        int szu=dfs(u,v);
        fd(j,min(m,sz),1)
            fd(k,min(m,szu),1)
                if(j+k<=m)
                {
                    upd(h[v][j+k],f[v][j]+f[u][k]+B[i][2]);
                    upd(h[v][j+k],h[v][j]+2*(g[u][k]+B[i][2]));
                    upd(h[v][j+k],h[u][k]+2*(g[v][j]+B[i][2]));
                    upd(f[v][j+k],f[v][j]+2*(g[u][k]+B[i][2]));
                    upd(f[v][j+k],f[u][k]+B[i][2]+2*g[v][j]);
                    upd(g[v][j+k],g[v][j]+g[u][k]+B[i][2]);
                }

        sz+=szu;
    }
    upd(ans,f[v][m]);upd(ans,h[v][m]);
    return sz;
}
int main()
{
    freopen("tree.in","r",stdin);
    freopen("tree.out","w",stdout);
    int x,y,z;
    read(n),read(m);
    fo(i,1,n-1) read(x),read(y),read(z),link(x,y,z),link(y,x,z);
    mset(f,60);mset(g,60);mset(h,60);
    ans=INF;
    dfs(1);
    printf("%d",ans);
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值