考虑如果给定一个K算答案怎么算,我们可以费用流,每个树边建流量INF费用1,每个点向T连流量c[x]费用0,对于前k个鼹鼠,S向每个鼹鼠出现的位置连一个流量1费用0的边,然后跑最小费用最大流即可求出最小代价
那么如果是对于每个K都要求的话,我们可以考虑令S连向第i个鼹鼠出现位置的边费用为(m-i+1)*INF,这样的话第i次曾广一定会走第i个鼹鼠出现的位置,那么増广i次之后得到的就是前i个鼹鼠的答案
注意是不能第i次増广之前新加一条S到第i个鼹鼠出现位置的边的,这样做的话会出负环
然而我们发现这个数据范围的话跑n遍spfa会T
那么就是一些在特殊图上用其他算法算网络流的套路了
我们发现这是一棵树,而每次找増广路就是在所有c>0的点中寻找一个离起点最近的,然后把那个点的c减一,并更新路径上的后向弧相关的信息,也就是把一些边的边权由1改成-1或者由-1改成1
这玩意乍一看挺不可做的,然而我们发现题目里还对树的形态作了特殊限制,i的父亲是i/2
那么这就很棒棒了,这棵树的树高是log的,那么任意两点间路径长度也是log级别的,并且每个点最多只有两个儿子
我们对每个点维护他子树里到他最近的c>0的点的距离以及是哪个点,然后枚举lca即可算出离开始点最近的c>0的点以及距离
然后暴力更新dp值和边权即可
复杂度O(n log n)
#include<iostream>
#include<cstring>
#include<ctime>
#include<cmath>
#include<algorithm>
#include<iomanip>
#include<cstdlib>
#include<cstdio>
#include<map>
#include<bitset>
#include<set>
#include<stack>
#include<vector>
#include<queue>
using namespace std;
#define MAXN 200010
#define MAXM 1010
#define ll long long
#define eps 1e-8
#define MOD 1000000007
#define INF 1000000000
int n,m;
int rem[MAXN];
int pos[MAXN];
int f[MAXN],ft[MAXN];
int vu[MAXN],vd[MAXN],cu[MAXN],cd[MAXN];
ll ans;
void ud(int x){
if(f[x<<1]<=f[x<<1|1]){
f[x]=f[x<<1];
ft[x]=ft[x<<1];
}else{
f[x]=f[x<<1|1];
ft[x]=ft[x<<1|1];
}
if(rem[x]&&f[x]>0){
f[x]=0;
ft[x]=x;
}
f[x]+=vd[x];
}
void find(int x){
int i;
int now=0;
int mn=f[x]-vd[x],mnt=ft[x],lca=x;
for(i=x;i!=1;i>>=1){
now+=vu[i];
if(now+f[i^1]<mn){
mn=now+f[i^1];
mnt=ft[i^1];
lca=i>>1;
}
if(rem[i>>1]&&now<mn){
mn=now;
mnt=i>>1;
lca=i>>1;
}
}
ans+=mn;
rem[mnt]--;
for(i=x;i!=lca;i>>=1){
if(vu[i]==1){
if(!cd[i]){
vd[i]=-1;
}
cd[i]++;
}
if(cu[i]){
if(!(--cu[i])){
vu[i]=1;
}
}
ud(i);
}
for(i=mnt;i!=lca;i>>=1){
if(vd[i]==1){
if(!cu[i]){
vu[i]=-1;
}
cu[i]++;
}
if(cd[i]){
if(!(--cd[i])){
vd[i]=1;
}
}
ud(i);
}
for(i=lca;i;i>>=1){
ud(i);
}
}
int main(){
int i,x;
memset(f,0x3f,sizeof(f));
scanf("%d%d",&n,&m);
for(i=1;i<=n;i++){
scanf("%d",&rem[i]);
}
for(i=n;i;i--){
vu[i]=vd[i]=1;
ud(i);
}
for(i=1;i<=m;i++){
scanf("%d",&x);
find(x);
printf("%lld ",ans);
}
printf("\n");
return 0;
}
/*
4 5
0 1 1 3
1 2 4 3 2
*/