题意 给了n个点的数 每个点有一个w[i]权值,如果你选择了i这个点那么距离i这个点距离为w[i]的点将被除去,最后问 选则尽量少的点把这n个点全部删除
1<=n<=100000, 0<=w<=100,
down[i][j]表示以i为根节点的树 在他的子树中在距离他 j距离 范围内存在至少一个点没有被除去所选择的最少点数
up[i][j] 表示以i为根的树 他的子树全部都被除去,并且距离他为j的其他点可被除去 所选择的最小点数
考虑状态转移
如果第i个点不选
那么
j=0时
down[i][0]=sigma(up[v][0]){v为i的孩子}
up[i][0] =min(up[i][0], up[v][1]+down[i][0]-up[v][0]){v为i的孩子}
j!=0的时候
down[i][j]=down[i][j]+down[v][j-1](v为i的孩子)
up[i][j]=min(up[i][j],up[v][j+1]+down[i][j]-down[v][j-1]){v为i的孩子 , 自然你也可以在他的孩子中在j范围内取更多的点,但是好好想想这样是没有必要的}
选了这个点
那么up[i][j]=min( up[i][j] , Sigma(G[v][w[i]-1]) ) {v为i的孩子,自然也可以选择更进的点 但是也是没有必要的 因为我们每次都更新了G[v][w[i]-1]的值 }
#include <iostream> #include <algorithm> #include <cstdio> #include <vector> #include <string.h> using namespace std; const int maxn =100000+5; const int maxm=100+5; int up[maxn][maxm],down[maxn][maxm],w[maxn]; vector<int>G[maxn]; int n; void dfs(int cur, int per) { for(int i=0; i<=100; i++)up[cur][i]=n; memset(down[cur],0,sizeof(down[cur])); int siz =G[cur].size(); int sum=1; for(int i=0; i<siz; i++) { int to=G[cur][i]; if(to==per)continue; dfs(to,cur); if(w[cur]) sum+=down[to][w[cur]-1]; else sum+=up[to][0]; down[cur][0]+=up[to][0]; for(int j=1; j<=100; j++) down[cur][j]+=down[to][j-1]; } for(int i=0; i<siz; i++) { int to=G[cur][i]; if(to==per)continue; up[cur][0]=min(up[cur][0],up[to][1]+down[cur][0]-up[to][0]); for(int j=1; j<100; j++) up[cur][j]=min(up[cur][j],up[to][j+1]+down[cur][j]-down[to][j-1]); } for(int i=0; i<=w[cur]; i++)up[cur][i]=min(up[cur][i],sum); for(int i=99; i>=0;i--)up[cur][i]=min(up[cur][i],up[cur][i+1]); down[cur][0]=min(down[cur][0],up[cur][0]); for(int i=1; i<=100; i++) down[cur][i]=min(down[cur][i],down[cur][i-1]); } int main() { while(scanf("%d",&n)==1) { for(int i=1; i<=n; i++) { scanf("%d",&w[i]); G[i].clear(); } for(int i=1; i<n; i++) { int a,b; scanf("%d%d",&a,&b); G[a].push_back(b); G[b].push_back(a); } dfs(1,0); int ans=n; for(int i=0; i<=100; i++)ans=min(ans,up[1][i]); printf("%d\n",ans); } return 0; }
时间: 2024-10-25 15:03:14