洛谷 P3527 [POI2011]MET-Meteors 整体二分+树状数组

Byteotian Interstellar Union有N个成员国。现在它发现了一颗新的星球,这颗星球的轨道被分为M份(第M份和第1份相邻),第i份上有第Ai个国家的太空站。 这个星球经常会下陨石雨。BIU已经预测了接下来K场陨石雨的情况。 BIU的第i个成员国希望能够收集Pi单位的陨石样本。你的任务是判断对于每个国家,它需要在第几次陨石雨之后,才能收集足够的陨石。

输入: 第一行是两个数N,M。 第二行有M个数,第i个数Oi表示第i段轨道上有第Oi个国家的太空站。 第三行有N个数,第i个数Pi表示第i个国家希望收集的陨石数量。 第四行有一个数K,表示BIU预测了接下来的K场陨石雨。 接下来K行,每行有三个数Li,Ri,Ai,表示第K场陨石雨的发生地点在从Li顺时针到Ri的区间中(如果Li<=Ri,就是Li,Li+1,…,Ri,否则就是Ri,Ri+1,…,m-1,m,1,…,Li),向区间中的每个太空站提供Ai单位的陨石样本。

输出: N行。第i行的数Wi表示第i个国家在第Wi波陨石雨之后能够收集到足够的陨石样本。如果到第K波结束后仍然收集不到,输出NIE。

数据范围: 1<=n,m,k<=3*10^5 1<=Pi<=10^9 1<=Ai<10^9

分析:
对于每一个国家我们可以二分一个答案。对于一个答案 m i d mid mid,我们把 l l l m i d mid mid的操作做了,然后枚举这个国家所有属于他的位置进行询问。因为每个位置只属于一个国家,最多询问 l o g log log次。可以使用树状数组维护区间加单点询问。复杂度是 O ( n l o g 2 n ) O(nlog^2n) O(nlog2n)的。

代码:

// luogu-judger-enable-o2
#include <iostream>
#include <cstdio>
#include <cmath>
#include <vector>
#define LL long long

const int maxn=3e5+7;

using namespace std;

int n,m,x,T;
int ans[maxn];
LL t[maxn];

vector <int> e[maxn];

struct que{
    int x,k;
}q[maxn],L[maxn],R[maxn];

struct rec{
    int l,r,k;
}a[maxn];

void updata(int x,int k)
{
    for (int i=x;i<=m;i+=i&(-i)) t[i]+=(LL)k;
}

LL query(int x)
{
    LL sum=0;
    for (int i=x;i>0;i-=i&(-i)) sum+=t[i];
    return sum;
}

void ins(int l,int r,int k)
{
    updata(l,k);
    if (r<m) updata(r+1,-k);
}

void solve(int l,int r,int ql,int qr)
{
    if (ql>qr) return;
    if (l==r)
    {
        for (int i=ql;i<=qr;i++) ans[q[i].x]=l;
        return;
    }
    int mid=(l+r)/2;
    for (int i=l;i<=mid;i++)
    {
        if (a[i].l<=a[i].r) ins(a[i].l,a[i].r,a[i].k);
                       else ins(a[i].l,m,a[i].k),ins(1,a[i].r,a[i].k);
    }
    int cnt1=0,cnt2=0;
    for (int i=ql;i<=qr;i++)
    {
        int x=q[i].x;
        LL sum=0;
        for (int j=0;j<e[x].size();j++)
        {
            int y=e[x][j];
            sum+=query(y);
            if (sum>=q[i].k) break;
        }
        if (sum>=q[i].k) L[++cnt1]=q[i];
        if (sum<q[i].k) q[i].k-=(int)sum,R[++cnt2]=q[i];
    }
    for (int i=l;i<=mid;i++)
    {
        if (a[i].l<=a[i].r) ins(a[i].l,a[i].r,-a[i].k);
                       else ins(a[i].l,m,-a[i].k),ins(1,a[i].r,-a[i].k);
    }
    for (int i=1;i<=cnt1;i++) q[ql+i-1]=L[i];
    for (int i=1;i<=cnt2;i++) q[ql+cnt1+i-1]=R[i];
    solve(l,mid,ql,ql+cnt1-1);
    solve(mid+1,r,ql+cnt1,qr);
}

int main()
{
    scanf("%d%d",&n,&m);
    for (int i=1;i<=m;i++)
    {
        scanf("%d",&x);
        e[x].push_back(i);
    }
    for (int i=1;i<=n;i++)
    {
        scanf("%d",&x);
        q[i]=(que){i,x};
    }
    scanf("%d",&T);
    for (int i=1;i<=T;i++) scanf("%d%d%d",&a[i].l,&a[i].r,&a[i].k);	    
    solve(1,T+1,1,n);
    for (int i=1;i<=n;i++)
    {
        if (ans[i]>T) printf("NIE\n");
                 else printf("%d\n",ans[i]);
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值