BZOJ 5072: [Lydsy1710月赛]小A的树 树形DP
这个题有一个结论:如果用 $x$ 个点能凑出的给点个数在 $[L,R]$ 之间,那么任意 $v\in [L,R]$ 一定能取到.
知道这个结论之后跑一个树形背包就行了,注意在跑背包的时候上界一定要限制好,要不然时间复杂度会多一个 $O(n)$ 的.
code:
#include <cstdio> #include <string> #include <cstring> #include <algorithm> #define N 5020 #define setIO(s) freopen(s".in","r",stdin) using namespace std; int n,edges; int hd[N],to[N<<1],nex[N<<1],size[N],f[N][N],g[N][N],ff[N],gg[N],v[N],sf[N],sg[N]; void add(int u,int v) { nex[++edges]=hd[u],hd[u]=edges,to[edges]=v; } void clr() { edges=0; memset(hd,0,sizeof(hd)); memset(nex,0,sizeof(nex)); } void dfs(int u,int fa) { size[u]=1; f[u][1]=g[u][1]=v[u]; for(int i=hd[u];i;i=nex[i]) { int y=to[i]; if(y==fa) continue; dfs(y,u); memcpy(ff,f[u],sizeof(f[u])); memcpy(gg,g[u],sizeof(g[u])); for(int j=1;j<=size[u];++j) { for(int k=1;k<=size[y];++k) { ff[j+k]=max(ff[j+k],f[u][j]+f[y][k]); gg[j+k]=min(gg[j+k],g[u][j]+g[y][k]); } } size[u]+=size[y]; for(int j=1;j<=size[u];++j) f[u][j]=ff[j], g[u][j]=gg[j]; } for(int i=1;i<=size[u];++i) sf[i]=max(sf[i],f[u][i]), sg[i]=min(sg[i],g[u][i]); } void solve() { int i,j,Q; scanf("%d%d",&n,&Q); for(i=1;i<n;++i) { int x,y; scanf("%d%d",&x,&y),add(x,y),add(y,x); } for(i=1;i<=n;++i) scanf("%d",&v[i]); memset(f,0xc0,sizeof(f)), memset(g,0x3f,sizeof(g)); memset(sf,0xc0,sizeof(sf)), memset(sg,0x3f,sizeof(sg)); dfs(1,0); for(i=1;i<=Q;++i) { int x,y; scanf("%d%d",&x,&y); if(y>=sg[x]&&y<=sf[x]) printf("YES\n"); else printf("NO\n"); } printf("\n"); clr(); } int main() { // setIO("input"); int i,j,T; scanf("%d",&T); while(T--) solve(); return 0; }