P3527 [POI2011]MET-Meteors

题意:

Link

给定一个环,每个节点有一个所属国家,k次事件,每次对[l,r]区间上的每个点点权加上一个值,求每个国家最早多少次操作之后所有点的点权和能达到一个值。

题解:

整体二分的经典题。

还是考虑二分答案,对询问分治。

这道题关键是怎么判断询问答案是否大于或小于 \(mid\) .

我们只需要看,他一共收集到的能量与 他需要的能量的大小。

如果大的话说明他的答案在 \(mid\) 左边,反之位于 \(mid\) 右边。

这就需要,我们支持一个区间修改,单点查询的数据结构, 树状数组 OR 线段树。

因为树状数组常数比较小,所以我这道题用的是树状数组。

至于 \(l > r\) 的情况,我们可以这么写

 chenge(c[i].l,c[i].w); chenge(c[i].r+1,-c[i].w);
 if(c[i].l > c[i].r) chenge(1,c[i].w);

这是一种比较神奇的写法(我从学长那里偷学过来的)。不理解的可以自己画一下图。

无解的情况,只需要我们在传参的时候答案的范围是 \(1-m+1\), 如果他的答案是 \(m+1\) 就代表无解

具体Code:

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<vector>
using namespace std;
#define int long long
const int N = 3e5+10;
vector<int> sta[N];
int n,m,k,x,ans[N],tr[N];
struct node
{
    int c,id;
}q[N],stal[N],star[N];
struct modify
{
    int l,r,w;
}c[N];
inline int read()
{
    int s = 0, w = 1; char ch = getchar();
    while(ch < '0' || ch > '9'){if(ch == '-') w = -1; ch = getchar();}
    while(ch >= '0' && ch <= '9'){s =s * 10+ch - '0'; ch = getchar();}
    return s * w;
}
int lowbit(int x){return x & -x;}
void chenge(int x,int val)//树状数组
{
    for(; x <= N-5; x += lowbit(x)) tr[x] += val; 
}
int ask(int x)
{
    int res = 0;
    for(; x; x -= lowbit(x)) res += tr[x];
    return res;
}
void work(int l,int r,int L,int R)
{
    int cntl = 0, cntr = 0, tmp = 0;
    if(l == r)
    {
        if(l != m+1)//他的答案不是无解
        {
            for(int i = L; i <= R; i++)
            {
                ans[q[i].id] = l;
            }
        }
        return;
    }
    int mid = (l+r)>>1;
    for(int i = l; i <= mid; i++)//区间修改树状数组
    {
        chenge(c[i].l,c[i].w); chenge(c[i].r+1,-c[i].w);//记得是 c[i].r + 1,不要写错了
        if(c[i].l > c[i].r) chenge(1,c[i].w);
    }
    for(int i = L; i <= R; i++)
    {
        tmp = 0;
        for(int j = 0; j < sta[q[i].id].size(); j++)//记住不要写成 sta[i].siz 要和询问的编号对应,
    //因为他在后面的顺序已经被我们打乱了
        {
            int x = sta[q[i].id][j];
            tmp += ask(x);
            if(tmp >= q[i].c) break;//避免爆 long long 
        }
        if(tmp >= q[i].c)
        {
            stal[++cntl] = q[i];
        }
        else
        {
            q[i].c -= tmp;
            star[++cntr] = q[i];
        }
    }
    for(int i = l; i <= mid; i++)//还原操作
    {
        chenge(c[i].l,-c[i].w); chenge(c[i].r+1,c[i].w);
        if(c[i].l > c[i].r) chenge(1,-c[i].w); 
    }
    for(int i = L; i <= L + cntl - 1; i++) q[i] = stal[i - L + 1];//给询问重新排个序
    for(int i = L + cntl; i <= R; i++) q[i] = star[i - L - cntl + 1];
    work(l,mid,L,L+cntl-1); work(mid+1,r,L+cntl,R);//继续二分
}
signed main()
{
    n = read(); m = read();
    for(int i = 1; i <= m; i++)
    {
        x = read();
        sta[x].push_back(i);//对每个节点的位置存入 Vector
    }
    for(int i = 1; i <= n; i++)
    {
        q[i].c = read();
        q[i].id = i;
    }
    m = read();
    for(int i = 1; i <= m; i++)
    {
        c[i].l = read();
        c[i].r = read();
        c[i].w = read();
    }
    work(1,m+1,1,n);//整体二分
    for(int i = 1; i <= n; i++) 
    {
        if(!ans[i]) puts("NIE");//无解的情况
        else printf("%lld\n",ans[i]);
    }
    return 0;
}

另外此题还有双倍经验 SP10264 METEORS - Meteors

据说 SPoj 的题,洛谷不管。(那是不是可以尽情的水题了,雾)

posted @ 2020-09-05 07:52  genshy  阅读(110)  评论(0编辑  收藏  举报