BZOJ4849 [Neerc2016]Mole Tunnels

来源:互联网 发布:什么是网络接口 编辑:程序博客网 时间:2024/06/08 17:41

考虑如果给定一个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 1000000000int 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 50 1 1 3 1 2 4 3 2 */