4849: [Neerc2016]Mole Tunnels

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 的答案肯定可以在k1的基础上,修改一些路径得到
将原图对应到一棵二叉树
对于每条隧道,记录当前方案中,
有多少只鼹鼠从下往上或是从上往下通过
显然这两种情况只能存在其中一种,否则可以相互抵消让答案更优
因为题目中边的限制,这棵二叉树的深度为 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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值