• 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
    ",ans[i]);
        }
        return 0;
    }
    

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

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

  • 相关阅读:
    算法设计策略
    挖金矿
    寻找假币
    循环赛日程表
    【三分钟视频教程】iOS开发中 Xcode 报 apple-o linker 错误的#解决方案#
    iOS问题#解决方案#之关于“application/x-www-form-urlencoded;charset=utf-8” not supported
    【两分钟教程】如何为博客园添加QQ咨询效果
    【两分钟教程】如何更改Xcode项目名称
    iOS数据库操作之coredata详细操作步骤
    svn使用笔记
  • 原文地址:https://www.cnblogs.com/genshy/p/13617010.html
Copyright © 2020-2023  润新知