【BZOJ3935】—RBTree(树形dp)

传送门


把距离的限制列出来好像可以单纯形做


考虑 f [ i ] [ j ] [ k ] f[i][j][k] f[i][j][k]表示 i i i的子树中有 j j j个黑点,最近的点为 k k k的最小代价(k不一定在 i i i子树内)

考虑转移边 u → v u\rightarrow v uv的时候,如果 u , v u,v u,v k k k一样就可以直接转移
否则是 u u u的在外面 v v v的在子树内,取 v v v子树内最小值即可
u , v u,v u,v的都在外面而且不一样的话显然是没用的状态
因为这时候 u , v u,v u,v最近的点一定相同

记一下 s i z siz siz可以做到 O ( n 3 ) O(n^3) O(n3)
喜提 b z o j   r k bzoj\ rk bzoj rk倒数第一

#include<bits/stdc++.h>
using namespace std;
const int RLEN=1<<20|1;
inline char gc(){
    static char ibuf[RLEN],*ib,*ob;
    (ob==ib)&&(ob=(ib=ibuf)+fread(ibuf,1,RLEN,stdin));
    return (ob==ib)?EOF:*ib++;
}
#define gc getchar
inline int read(){
    char ch=gc();
    int res=0,f=1;
    while(!isdigit(ch))f^=ch=='-',ch=gc();
    while(isdigit(ch))res=(res+(res<<2)<<1)+(ch^48),ch=gc();
    return f?res:-res;
}
#define ll long long
#define re register
#define pii pair<int,int>
#define fi first
#define se second
#define pb push_back
#define cs const
#define bg begin
inline void chemx(int &a,int b){a<b?a=b:0;}
inline void chemn(int &a,int b){a>b?a=b:0;}
cs int N=505;
ll dis[N][N],*dep;
int in[N],out[N],idx[N],dfn,inf,tot,siz[N];
int adj[N],nxt[N<<1],to[N<<1],val[N<<1],cnt;
int n,f[N][N][N],g[N][N],c[N],mxdis;
inline void addedge(int u,int v,int w){
	nxt[++cnt]=adj[u],adj[u]=cnt,to[cnt]=v,val[cnt]=w;
}
void dfs(int u,int fa){
	for(int e=adj[u];e;e=nxt[e]){
		int v=to[e];
		if(v==fa)continue;
		dep[v]=dep[u]+val[e],dfs(v,u);
	}
}
void dfs2(int u,int fa){
	in[u]=++dfn,idx[dfn]=u;
	for(int e=adj[u];e;e=nxt[e]){
		int v=to[e];
		if(v==fa)continue;
		dfs2(v,u);
	}
	out[u]=dfn;
}
void dp(int u,int fa){
	siz[u]=1;
	if(c[u])f[u][1][u]=0;
	else f[u][1][u]=1;
	for(int i=1;i<=n;i++)if(i!=u&&dis[i][u]<=mxdis)f[u][0][i]=0;
	for(int e=adj[u];e;e=nxt[e]){
		int v=to[e];
		if(v==fa)continue;
		dp(v,u);
		memset(g,127/3,sizeof(g));
		for(int x=0;x<=siz[u];x++)
		for(int y=0;y<=siz[v];y++){
			for(int k=1;k<=n;k++)chemn(g[x+y][k],f[u][x][k]+f[v][y][k]);
			int res=inf;
			for(int k=in[v];k<=out[v];k++)chemn(res,f[v][y][idx[k]]);
			for(int k=1;k<in[v];k++)chemn(g[x+y][idx[k]],f[u][x][idx[k]]+res);
			for(int k=out[v]+1;k<=n;k++)chemn(g[x+y][idx[k]],f[u][x][idx[k]]+res);
		}
		siz[u]+=siz[v],memcpy(f[u],g,sizeof(g));
	}
}
int main(){
	n=read(),mxdis=read();
	for(int i=1;i<=n;i++)c[i]=read(),tot+=c[i]==1;
	for(int i=1;i<n;i++){
		int u=read(),v=read(),w=read();
		addedge(u,v,w),addedge(v,u,w);
	}
	for(int i=1;i<=n;i++)dep=dis[i],dfs(i,0);
	memset(f,127/3,sizeof(f));inf=f[0][0][0];
	dfs2(1,0);
	dp(1,0);
	int res=inf;
	for(int i=1;i<=n;i++)chemn(res,f[1][tot][i]);
	if(res==inf)cout<<-1;
	else cout<<res;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值