【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的时候写

浙公网安备 33010602011771号