特别玄学的一道题

点击打开链接

题意:给你n个点,每个点有一个以他自身为中心的范围,求每个数能扩散的最大范围,扩散具有传递性!

官方题解:

很多人的做法是先对每个点找出最右边传染病可以到达的位置,再找出最左边可以达到的位置,然后二者相减。
这样做很明显的问题是你最右边可以达到的位置可能是在你左边的人所传染的。

一种做法是可以二分预处理出来每个点单次传染到的左右范围,再用线段树维护每个点最终的范围最小值、最大值,利用线段树不断扩大范围并更新。你可以随机一个 1 ~ n 的序列利用线段树更新,这样速度会变得很快,或其他优化方法均可。

这里提供另一种很奇怪的方法:从左往右扫,用一个 latest 保证一个点只计算一次。在计算每个点的过程中,先把当前点的影响范围暂存为它左边影响的点的影响范围并上它本身的影响范围。然后对它本身影响范围内的点没有计算过的进行递归计算,计算完了之后重新统计。
很容易产生的一个问题就是如果递归的点计算需要这个点的数据怎么办?我们认为这种情况是不影响的。因为事实上,这个点的数据之所以不对是因为它后面的点还没有进行计算。我们会把后面的点都并到正在计算的点的计算范围,然后我们还是会对后面的点进行计算。
同样用线段树维护查询。

黑科技:可以直接暴力的考虑,对于可能从某个点左边影响其右边的情况,或者从右边回来的情况,其实是log级别的。
所以每次o(n)去递推每个点的最左和最右,然后再考虑左右影响log(n)次即可。

不知道这是什么定理,但是的确对。也可能这题数据水,只左右t贪心扫两次都可以过题。

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<math.h>
#include<iostream>
#include<algorithm>
#include<stack>
#include<queue>
#include<vector>
#include<set>
#include<map>
#include<string>
using namespace std;
typedef long long ll;
typedef pair<int,int> P;
const int INF=0x3f3f3f3f;
int n;
P a[100010],b[100010];
int l[100010],r[100010];
void solve1()
{
    stack<int>s;
    for(int i=1;i<=n;i++)
    {
        while(!s.empty()&&s.top()>=l[i])
        {
            l[i]=min(l[i],l[s.top()]);
            s.pop();
        }
        s.push(i);
    }
    while(!s.empty())s.pop();
    for(int i=n;i>=1;i--)
    {
        while(!s.empty()&&s.top()<=r[i])
        {
            r[i]=max(r[i],r[s.top()]);
            s.pop();
        }
        s.push(i);
    }
}
void solve2()
{
    stack<int>s;
    for(int i=1;i<=n;i++)
    {
        while(!s.empty()&&s.top()>=l[i])
        {
            r[i]=max(r[i],r[s.top()]);
            s.pop();
        }
        s.push(i);
    }
    while(!s.empty())s.pop();
    for(int i=n;i>=1;i--)
    {
        while(!s.empty()&&s.top()<=r[i])
        {
            l[i]=min(l[i],l[s.top()]);
            s.pop();
        }
        s.push(i);
    }
}
int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++){scanf("%d%d",&a[i].first,&a[i].second);b[i]=a[i];}
    sort(a+1,a+n+1);
    for(int i=1;i<=n;i++)
    {
        l[i]=lower_bound(a+1,a+1+n,P(a[i].first-a[i].second,-1))-a;
        r[i]=lower_bound(a+1,a+1+n,P(a[i].first+a[i].second,INF))-a-1;
    }
    for(int i=1;i<=17;i++)//看似是n^2,实际只需要更新logn次就足够了,不知道这是什么定理,但是的确是对的!
        solve1(),solve2();
    for(int i=1;i<=n;i++)
    {
        int pos=lower_bound(a+1,a+n+1,b[i])-a;
        printf("%d%c",r[pos]-l[pos]+1,i==n?'\n':' ');
    }
    return 0;
}

正规解:

#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、付费专栏及课程。

余额充值