2017 ECNU 网赛 D. 实验室传染病

熟悉啊各位,看到这题的时候有没有一种TMD绝壁看过但就是不会做的感觉啊?!!

比赛的时候G题蜜汁精度又WA又T38发,最后也没有好好想这道题,赛后好好考虑了一下,其实思路挺清晰的,不过还是又一些坑点,又WA又T了好多发才过 TAT,解法也是玄学啊。

思路:
大致的思路就是,预先处理出每个点一次扩展能到达的范围,代码中用 far_l[],far_r[] 表示,然后用一个线段树维护每个区间的往左往右最多能感染的范围。
一开始线段树里先放入扩展一次的左右点,然后维护。
对于点 i 的查询,一开始假设感染区间为为[ far_l[i],far_r[i] ],然后在线段树上查询这个区间内的点往左往右最多扩增到哪。
不断重复上述扩增操作,直到不能再扩增了位置。
假设得到 answer[i]=[l,r] 需要把这个点在线段树上包含这个点的区间更新为 [l,r] ,因为如果你不更新要不然直接出 {i,1} 这样一组数据就直接炸了。
但是以为这样就可以了吗? 并不!
还是有数据可以卡你,死活T在一组数据上!虽然我也不知道数据到底是什么,但是可以想到可能是类似于有序序列对于 QSort 一样被卡死,所以 shuffle 一下。
done.
思路:

#include <set>
#include <map>
#include <queue>
#include <cmath>
#include <vector>
#include <cstdio>
#include <utility>
#include <cstring>
#include <iostream>
#include <algorithm>
const int maxn = 100007;

int far_l[maxn], far_r[maxn], n;

struct SegTree
{
    int l, r, far_l, far_r;
}seg[maxn * 4];

void build(int l, int r, int k)
{
    seg[k].l = l;
    seg[k].r = r;
    if(l == r) {
        seg[k].far_l = far_l[l];
        seg[k].far_r = far_r[r];
        return;
    }
    int mid = (l + r) / 2;
    build(l, mid, 2 * k);
    build(mid + 1, r, 2 * k + 1);
    seg[k].far_l = std::min(seg[2*k].far_l, seg[2*k+1].far_l);
    seg[k].far_r = std::max(seg[2*k].far_r, seg[2*k+1].far_r);
}

std::pair<int, int> cal(int l, int r, int k)
{
    if(l <= seg[k].l && seg[k].r <= r) {
        return {seg[k].far_l, seg[k].far_r};
    }
    int mid = (seg[k].l + seg[k].r) / 2;
    if(r <= mid) {
        return cal(l, r, 2*k);
    } else if(l > mid) {
        return cal(l, r, 2*k+1);
    } else {
        auto a = cal(l, mid, 2*k);
        auto b = cal(mid+1, r, 2*k+1);
        return {std::min(a.first, b.first), std::max(a.second, b.second)};
    }
}

void update(int t, int k, int l, int r)
{
    if(seg[k].l == seg[k].r) {
        seg[k].far_l = l;
        seg[k].far_r = r;
        return;
    }
    int mid = (seg[k].l + seg[k].r) / 2;
    if(t <= mid) update(t, 2*k, l, r);
    else update(t, 2*k+1, l, r);
    seg[k].far_l = std::min(seg[2*k].far_l, seg[2*k+1].far_l);
    seg[k].far_r = std::max(seg[2*k].far_r, seg[2*k+1].far_r);
}

std::pair<int,int> query(int t)
{
    int l = far_l[t], r = far_r[t];
    int count = 0;
    while(true) {
        auto rec = cal(l, r, 1);
        if(l == rec.first && r == rec.second) break;
        l = rec.first, r = rec.second;
        count ++;
    }
    update(t, 1, l, r);
    return {l, r};
}

struct Node
{
    int a, b, id;
}node[maxn];

int ans[maxn];

bool cmp(const Node &_a, const Node &_b)
{
    return _a.a < _b.a;
}

int Scan()///输入外挂
{
    int res=0,ch,flag=0;
    if((ch=getchar())=='-')
        flag=1;
    else if(ch>='0'&&ch<='9')
        res=ch-'0';
    while((ch=getchar())>='0'&&ch<='9')
        res=res*10+ch-'0';
    return flag?-res:res;
}

int main()
{
    n = Scan();
    for(int i = 1; i <= n; i ++) {
        node[i].a = Scan();
        node[i].b = Scan();
        node[i].id = i;
    }
    std::sort(node+1, node+n+1, cmp);
    for(int i = 1; i <= n; i ++) {
        int l = i, r = n;
        while(l <= r) {
            int mid = (l + r) / 2;
            if(node[mid].a - node[i].a <= node[i].b) {
                far_r[i] = mid;
                l = mid + 1;
            } else r = mid - 1;
        }
    }
    for(int i = 1; i <= n; i ++) {
        int l = 1, r = i;
        while(l <= r) {
            int mid = (l + r) / 2;
            if(node[i].a - node[mid].a <= node[i].b) {
                far_l[i] = mid;
                r = mid - 1;
            } else l = mid + 1;
        }
    }
    build(1, n, 1);
    std::vector<int> vec;
    for(int i = 1; i <= n; i ++) {
        vec.push_back(i);
    }
    std::random_shuffle(vec.begin(), vec.end());
    for(int i = 0; i < n; i ++) {
        int id = vec[i];
        auto k = query(id);
        k = cal(k.first, k.second, 1);
        ans[node[id].id] = k.second - k.first + 1;
    }
    for(int i = 1; i <= n; i ++) {
        printf("%d%c", ans[i], i == n ? '\n' : ' ');
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值