4849: [Neerc2016]Mole Tunnels
Time Limit: 20 Sec Memory Limit: 128 MB
Submit: 34 Solved: 15
[Submit][Status][Discuss]
Description
鼹鼠们在底下开凿了n个洞,由n-1条隧道连接,对于任意的i>1,第i个洞都会和第i/2(取下整)个洞间有一条隧
道,第i个洞内还有ci个食物能供最多ci只鼹鼠吃。一共有m只鼹鼠,第i只鼹鼠住在第pi个洞内,一天早晨,前k只
鼹鼠醒来了,而后n-k只鼹鼠均在睡觉,前k只鼹鼠就开始觅食,最终他们都会到达某一个洞,使得所有洞的ci均大
于等于该洞内醒着的鼹鼠个数,而且要求鼹鼠行动路径总长度最小。现对于所有的1<=k<=m,输出最小的鼹鼠行动
路径的总长度,保证一定存在某种合法方案。
Input
第一行两个数n,m(1<=n,m<=100000),表示有n个洞,m只鼹鼠。
第二行n个整数ci表示第i个洞的食物数。
第三行m个整数pi表示第i只鼹鼠所在洞pi。
Output
输出一行m个整数,第i个整数表示当k=i时最小的鼹鼠行动路径总长度。
Sample Input
5 4
0 0 4 1 1
2 4 5 2
Sample Output
1 1 2 4
HINT
Source
考虑贪心的策略
k 的答案肯定可以在k−1 的基础上,修改一些路径得到
将原图对应到一棵二叉树
对于每条隧道,记录当前方案中,
有多少只鼹鼠从下往上或是从上往下通过
显然这两种情况只能存在其中一种,否则可以相互抵消让答案更优
因为题目中边的限制,这棵二叉树的深度为 log2n
暴力枚举第 k 只鼹鼠可能的路径
先枚举它往上走的步数,然后往下找到目标点
对于每条隧道,如果这只鼹鼠要从下往上通过,
而之前已经有x(x>0) 只鼹鼠从上往下通过
那么我们通过交换这只鼹鼠和任意一只鼹鼠的后续路径
这条边通过的鼹鼠数量就减少了
于是这样的边权值可以标为 −1
每次找出路径都可以转换为二叉树上的区间修改
dfs序+线段数大力维护一下,每次贪心就行了
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cstring>
#include<vector>
#include<queue>
#include<set>
#include<map>
#include<stack>
#include<bitset>
#include<ext/pb_ds/priority_queue.hpp>
#define min(a,b) ((a) < (b) ? (a) : (b))
using namespace std;
const int T = 4;
const int INF = ~0U>>1;
const int maxn = 1E5 + 10;
int n,m,dfs_clock,now,len,Ans,dfn[maxn],End[maxn],c[maxn],val[maxn*T]
,Name[maxn],L[maxn],A[maxn],Num[maxn*T],Min[maxn*T],Mark[maxn*T];
inline void Dfs(int x)
{
dfn[x] = ++dfs_clock; Name[dfs_clock] = x;
if ((x<<1) <= n) L[x<<1] = L[x] + 1,Dfs(x<<1);
if ((x<<1|1) <= n) L[x<<1|1] = L[x] + 1,Dfs(x<<1|1);
End[x] = dfs_clock;
}
inline void maintain(int o)
{
int lc = (o << 1),rc = (o << 1 | 1);
if (Min[lc] < Min[rc])
Min[o] = Min[lc],Num[o] = Num[lc];
else Min[o] = Min[rc],Num[o] = Num[rc];
}
inline void pushdown(int o,int l,int r)
{
if (!Mark[o]) return;
if (Min[o] != INF) Min[o] += Mark[o];
if (l == r) {val[o] += Mark[o]; Mark[o] = 0; return;}
Mark[o<<1] += Mark[o]; Mark[o<<1|1] += Mark[o]; Mark[o] = 0;
}
inline void Build(int o,int l,int r)
{
if (l == r)
{
if (!c[Name[l]]) Min[o] = INF;
else Min[o] = L[Name[l]],Num[o] = Name[l];
val[o] = L[Name[l]]; return;
}
int mid = l + r >> 1;
Build(o<<1,l,mid); Build(o<<1|1,mid+1,r);
maintain(o);
}
inline void Search(int o,int l,int r,int ql,int qr)
{
pushdown(o,l,r);
if (Min[o] >= len) return;
if (ql <= l && r <= qr)
{
if (Min[o] < len)
len = Min[o],now = Num[o];
return;
}
int mid = l + r >> 1;
if (ql <= mid) Search(o<<1,l,mid,ql,qr);
if (qr > mid) Search(o<<1|1,mid+1,r,ql,qr);
}
inline int Query(int o,int l,int r,int pos)
{
pushdown(o,l,r); if (l == r) return val[o]; int mid = l + r >> 1;
return pos <= mid ? Query(o<<1,l,mid,pos) : Query(o<<1|1,mid+1,r,pos);
}
inline void Delete(int o,int l,int r,int pos)
{
pushdown(o,l,r);
if (l == r)
{
Min[o] = INF; Num[o] = 0; return;
}
int mid = l + r >> 1;
if (pos <= mid) Delete(o<<1,l,mid,pos); else pushdown(o<<1,l,mid);
if (pos > mid) Delete(o<<1|1,mid+1,r,pos); else pushdown(o<<1|1,mid+1,r);
maintain(o);
}
inline void Modify(int o,int l,int r,int ql,int qr,int k)
{
if (ql <= l && r <= qr)
{
Mark[o] += k; pushdown(o,l,r); return;
}
int mid = l + r >> 1; pushdown(o,l,r);
if (ql <= mid) Modify(o<<1,l,mid,ql,qr,k); else pushdown(o<<1,l,mid);
if (qr > mid) Modify(o<<1|1,mid+1,r,ql,qr,k); else pushdown(o<<1|1,mid+1,r);
maintain(o);
}
inline void Walk(int x,int y)
{
--c[y]; if (!c[y]) Delete(1,1,n,dfn[y]);
while (L[x] > L[y])
{
if (!A[x]) Modify(1,1,n,dfn[x],End[x],-2);
++A[x]; x >>= 1;
}
while (L[y] > L[x])
{
if (A[y] == 1) Modify(1,1,n,dfn[y],End[y],2);
--A[y]; y >>= 1;
}
while (x != y)
{
if (!A[x]) Modify(1,1,n,dfn[x],End[x],-2);
if (A[y] == 1) Modify(1,1,n,dfn[y],End[y],2);
++A[x]; x >>= 1; --A[y]; y >>= 1;
}
}
inline int getint()
{
char ch = getchar(); int ret = 0;
while (ch < '0' || '9' < ch) ch = getchar();
while ('0' <= ch && ch <= '9')
ret = ret * 10 + ch - '0',ch = getchar();
return ret;
}
int main()
{
#ifdef DMC
freopen("DMC.txt","r",stdin);
freopen("test.txt","w",stdout);
#endif
n = getint(); m = getint();
for (int i = 1; i <= n; i++) c[i] = getint();
Dfs(1); Build(1,1,n);
for (int i = 1; i <= m; i++)
{
int x = getint(),y,mi,sum;
len = INF; Search(1,1,n,dfn[x],End[x]);
if (len != INF) len -= Query(1,1,n,dfn[x]);
y = now; mi = len; sum = A[x] >= 0 ? 1 : -1;
for (int z = x >> 1,pos = x & 1; z; pos = z & 1,sum += A[z] >= 0 ? 1 : -1,z >>= 1)
{
int Nex = (z << 1) | (pos ^ 1); len = INF;
if (c[z] > 0 && sum < mi) mi = sum,y = z;
if (Nex > n) continue;
Search(1,1,n,dfn[Nex],End[Nex]);
if (len == INF) continue;
len -= Query(1,1,n,dfn[z]);
if (len + sum < mi) mi = len + sum,y = now;
}
Walk(x,y); Ans += mi; printf("%d ",Ans);
}
return 0;
}