【LGR-236-Div.2】洛谷 8 月月赛 II & IAMOI Round #2

P13680 [IAMOI R2] 未送出的花

发现一定是大根堆,这个题解讲的好
考虑使用调整法,假设现在有一个儿子的盛开度大于父亲的盛开度,现在尝试交换儿子和父亲的盛开度:
对于儿子的子树中的节点,根到它们的路径的盛开度序列不变,美丽值不发生变化。
对于父亲子树中且非儿子子树中的节点,根到它们的路径只经过父亲而不经过那个儿子,所以盛开度序列中有且仅有一个数会变大,美丽值不降。
对于非父亲子树中的节点,没有变化。
这样每个点到根的路径上的点权值都是单调的,倍增可以求每个点作为中位数的次数cnt

状态设计还是f[i][j],i,选了j个点的cnt最大
将dfs序翻转过来,就可以从下往上求,[i-sz[id[i]]+1,i]就是i的子树
f[i][j]=max(f[i][j],f[i-1][j-1]+cnt[id[i]]);//选这个点
f[i][j]=max(f[i][j],f[i-sz[id[i]]][j]);//不选这个点,这个点子树都不选

最后要找这朵花,在1的子树中找选了几个能达到总次数=k

#include<bits/stdc++.h>
using namespace std;


const int N=10010;
int n;
vector<int> G[N];
int dep[N];int fa[N][21];int cnt[N];int sz[N];
int id[N];int dfn;

int f[N][N];

void dfs(int u,int father){
id[++dfn] = u; 
dep[u]=dep[father]+1;
sz[u]=1;
 
for(int i=1;(1<<i)<=dep[u];i++)
    fa[u][i]=fa[fa[u][i-1]][i-1];

for(auto v:G[u]){
    if(v==father)continue;
    fa[v][0]=u;
    dfs(v,u);
    sz[u]+=sz[v];
}
int cur=u;
int len=dep[cur]-(dep[cur]+1)/2;
for(int i=20;i>=0;i--)
if(len&(1<<i))cur=fa[cur][i];
cnt[cur]++;
}


signed main(){
    std::ios::sync_with_stdio(false);
   int T;cin>>T;
   while (T--)
   {
    cin>>n;for(int i=1;i<=n;i++){G[i].clear();
        cnt[i]=0;}
    for(int i=1;i<=n-1;i++){
        int u,v;cin>>u>>v;
        G[u].push_back(v);G[v].push_back(u);
    }
   
    fa[1][0]=1;
    dfs(1,0);
    reverse(id + 1, id + dfn + 1);


    for(int i=1;i<=n;i++)for(int j=1;j<=n;j++)f[i][j]=0;
    for(int i=1;i<=n;i++){
        for(int j=1;j<=i;j++){
        f[i][j]=max(f[i][j],f[i-1][j-1]+cnt[id[i]]);
        f[i][j]=max(f[i][j],f[i-sz[id[i]]][j]);
        }
    }
    for(int i=1;i<=n;i++){
        int pos=lower_bound(f[n]+1,f[n]+n+1,i)-f[n];
        cout<<n-pos+1<<" ";
    }cout<<'\n';

   }
   
}

也可以按树上背包dfs的时候写

posted @ 2025-08-17 16:59  arin876  阅读(20)  评论(0)    收藏  举报