Codeforces 609F Frogs and mosquitoes 二分+简单离散化线段树+Multimap

题意

  • 有n青蛙和m蚊子(n,m<=1e5),青蛙两个参数,位置xi,舌头长度ti
  • 蚊子两个参数,位置pj,权值bj
  • 只有当xi+ti>=pj且xi <= pj时,第i只青蛙才能吃到第j只蚊子。
  • 如果第j只蚊子能被多只青蛙吃到,那么xi最小的青蛙会把这只蚊子吃了
  • 每次青蛙吃了某只蚊子以后,ti会增加蚊子的bj
  • 蚊子会按输入的顺序到来,如果没一下没吃掉,蚊子还是会落在原位不动,即蚊子有可能之后被吃掉
  • 输出n只青蛙吃的蚊子数,以及最后时候的舌头长度ti

思路

  • 先考虑每来一只蚊子时,我们怎么找到是哪只青蛙吃掉的呢?
  • 是不是找到,最左的位置l,使得[0, l]里青蛙xi + ti的最大值 > 蚊子位置pj, 并且xi <= pj即可
  • 由于位置l,和[0,l]中最大值有单调关系所以,我们通过二分很容易找到位置l
  • 另一方面,吃的青蛙,舌头会变长,即存在点修改和刚才说得区间查询,即用线段树可解
  • 随便一想,可能会觉得用位置做下标建树挺好,但是呢,位置到1e9,存不下,所以简单离散化一下,把青蛙按照xi进行排序,以青蛙数组下标建树,维护xi+ti的最大值,然后二分青蛙数组的下标即可。
  • 最后注意:蚊子是有可能之后被吃掉的!所以呢,还需要维护没被吃掉蚊子的位置
  • 这里,不难想到,用平衡二叉搜索树(multimap)解决即可。注意:一定是mutimap,因为蚊子可以落在同一点上!我被这点坑半天。。。

实现

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 2e5+6;

multimap<int,int> mapp;
ll maxv[3*maxn];

struct Node{
    int x, id;
    ll sum;
}a[maxn];

ll init(int v,int l,int r){
    if (l == r){
        maxv[v] = a[l].sum;
        return a[l].sum;
    }
    int chl = 2*v+1, chr = 2*v+2, mid = (l+r) / 2;
    maxv[v] = max(init(chl, l, mid), init(chr, mid+1, r));
    return maxv[v];
}
ll query(int u,int l,int r, int x1, int x2){
    if (x2 < l || x1 > r){
        return 0;
    }
    if (x1 <= l && x2 >= r){
        return maxv[u];
    }
    int chl = 2*u+1, chr = 2*u+2, mid = (l+r) / 2;
    return max(query(chl, l, mid,x1,x2), query(chr, mid+1, r, x1, x2));
}

ll add(int u,int l,int r,int x,int y){
    if (l == r){
        a[x].sum += (ll)y;
        maxv[u] += (ll)y;
        return maxv[u]; 
    }
    int chl = 2*u+1, chr = 2*u+2, mid = (l+r) / 2;
    if (x <= mid){
        return maxv[u] = max(maxv[u], add(chl, l, mid, x, y));
    }
    else{
        return maxv[u] = max(maxv[u], add(chr, mid+1, r, x , y));
    }
}


bool cmp(Node x, Node y){
    return x.x < y.x;
}

int num[maxn];
ll len[maxn];

int n,m;



int main(){
    scanf("%d%d",&n,&m);
    for (int i = 0;i < n;i++){
        int x,l;
        scanf("%d%d",&x, &l);
        a[i].sum = (ll)x + (ll)l;
        len[i] = (ll)l;
        a[i].x = x;
        a[i].id = i;
    }
    sort(a,a+n,cmp);

    init(0, 0, n-1);

    for (int t=0; t < m;t++){
        int p,b;
        scanf("%d%d",&p,&b);
        int low = 0, high = n - 1;
        int ans = -1;
        while (low <= high){
            int mid = (low + high) / 2;
            if (query(0, 0, n-1, 0, mid) >= p){
                high = mid - 1;
                ans = mid;
            }
            else{
                low = mid + 1;
            }
        }
        if (ans == -1 || a[ans].x > p){
            mapp.insert(make_pair(p,b));
            continue;
        }
        num[a[ans].id]++;
        len[a[ans].id] += (ll)b;
        add(0, 0, n-1, ans, b);
        auto it = mapp.lower_bound(a[ans].x);
        while (it != mapp.end() && it->first <= a[ans].sum){
            num[a[ans].id]++;
            len[a[ans].id] += (ll)it->second;
            add(0, 0, n-1, ans, it->second);
            auto tmp = it;
            it++;
            mapp.erase(tmp);
        }
    }
    for(int i=0;i<n;i++){
        printf("%d %I64d\n",num[i],len[i]);
    }   
    return 0;
} 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值