zoukankan      html  css  js  c++  java
  • [2021.10.4NOIP模拟] 切题

    前言

    好题啊,这很NOIP。

    题目

    模拟赛的题,我就不放出(搬)题人的fAKe题面了,可以去 CF 看原题。

    题目大意:

    ( t LLL) 对假期里同学们的表现不是很满意,于是她决定来 (N) 次突击检查,每次她会挑选 (a_i) 个同学(显然是不同的)到她的办公室,然而同学们的忍耐程度是有极限的,对于这 (M) 个同学来说,每个人有一个忍耐程度 (b_i),表示他/她最多能忍受被 ( t LLL) 叫去办公室的次数,一旦到达极限,他/她将逃离教室,同学们在没有达到极限的时候会尽可能满足 ( t LLL) 的要求。

    但随着时间的推移,( t LLL) 的抽查和同学们的程度都会有所变化。

    具体的,共有 (Q) 个时间点,每个时间点到达后会有以下四种变化情况:

    • (1 i)(a_i) 加一。
    • (2 i)(a_i) 减一。
    • (3 j)(b_j) 加一。
    • (4 j)(b_j) 减一。

    对于每个时间点,你需要回答同学们是否能够满足 ( t LLL) 的抽查要求。

    (1le N,M,Qle 250000;0le a_i,b_ile 250000.)

    数据保证任意时刻 (a,b) 非负。

    讲解

    首先试图用一个式子来表示合法时的情况:

    (a) 从大到小排序后,当且仅当 (forall kin[1,N],sum_{i=1}^k a_ile sum_{i=1}^Mmin(b_i,k))

    具体证明可以去看 _Wallace_巨佬 的博客。

    我们对每个 (k) 维护 (sum_{i=1}^Mmin(b_i,k)-sum_{i=1}^ka_i),但是直接做不好做,考虑转换。

    (c_k=sum_{i=1}^M[b_ige k]),则上面的式子可以化为 (sum_{i=1}^k(c_i-a_i)),是一个很舒服的式子,用线段树很好维护。

    • 对于 (a) 的改动,由于每次只改 (1),所以二分出最前面/后面那个点直接改就好,单调不降性质依然保持。
    • 对于 (b) 的改动,显然只会对 (1)(c) 有影响,可以直接改。

    时间复杂度 (O(nlog_2n))

    代码

    普通线段树
    //12252024832524
    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    #define TT template<typename T>
    using namespace std;
     
    typedef long long LL;
    const int MAXN = 250005;
    const LL INF = 1ll << 60;
    int n,m;
    int a[MAXN],b[MAXN],nowa[MAXN];
    LL cha[MAXN];
     
    LL Read()
    {
        LL x = 0,f = 1; char c = getchar();
        while(c > '9' || c < '0'){if(c == '-') f = -1;c = getchar();}
        while(c >= '0' && c <= '9'){x = (x*10) + (c^48);c = getchar();}
        return x * f;
    }
    TT void Put1(T x)
    {
        if(x > 9) Put1(x/10);
        putchar(x%10^48);
    }
    TT void Put(T x,char c = -1)
    {
        if(x < 0) putchar('-'),x = -x;
        Put1(x); if(c >= 0) putchar(c);
    }
    TT T Max(T x,T y){return x > y ? x : y;}
    TT T Min(T x,T y){return x < y ? x : y;}
    TT T Min(T x){return x < 0 ? -x : x;}
     
    #define lc (x<<1)
    #define rc (x<<1|1)
    LL MIN[MAXN << 2],lz[MAXN << 2];
    void calc(int x,int val){MIN[x] += val; lz[x] += val;}
    void up(int x){MIN[x] = Min(MIN[lc],MIN[rc]);}
    void down(int x){if(!lz[x]) return; calc(lc,lz[x]); calc(rc,lz[x]); lz[x] = 0;}
    void Add(int x,int l,int r,int ql,int qr,int val)
    {
        if(ql > qr) return;
        if(ql <= l && r <= qr)
        {
            calc(x,val);
            return;
        }
        int mid = (l+r) >> 1;
        down(x);
        if(ql <= mid) Add(lc,l,mid,ql,qr,val);
        if(mid+1 <= qr) Add(rc,mid+1,r,ql,qr,val);
        up(x);
    }
     
    int main()
    {
    //	freopen("problem.in","r",stdin);
    //	freopen("problem.out","w",stdout);
        n = Read(); m = Read();
        for(int i = 1;i <= n;++ i) nowa[i] = a[i] = Read();
        for(int i = 1;i <= m;++ i) b[i] = Read(),--cha[b[i]+1],++cha[1]; 
        sort(nowa+1,nowa+n+1,[](int x,int y){return x > y;});
        for(int i = 1;i <= n;++ i) Add(1,1,n,i,n,-nowa[i]);
        for(int i = 1;i <= n;++ i) cha[i] += cha[i-1],Add(1,1,n,i,n,cha[i]);
        for(int Q = Read(); Q ;-- Q)
        {
            int opt = Read(),pos = Read();
            if(opt == 1) 
            {
                int v = a[pos]; ++a[pos];
                int l = 1,r = n,ret = 1;
                while(l <= r)
                {
                    int mid = (l+r) >> 1;
                    if(nowa[mid] <= v) r = mid-1,ret = mid;
                    else l = mid+1;
                }
                Add(1,1,n,ret,n,-1); 
                ++nowa[ret];
            }
            else if(opt == 2)
            {
                int v = a[pos]; --a[pos];
                int l = 1,r = n,ret = 1;
                while(l <= r)
                {
                    int mid = (l+r) >> 1;
                    if(nowa[mid] >= v) l = mid+1,ret = mid;
                    else r = mid-1;
                }
                Add(1,1,n,ret,n,1);
                --nowa[ret];
            }
            else if(opt == 3)
            {
                ++b[pos];
                Add(1,1,n,b[pos],n,1);
            }
            else
            {
                Add(1,1,n,b[pos],n,-1);
                --b[pos];
            }
            if(MIN[1] >= 0) Put(1,'
    ');
            else Put(0,'
    ');
        }
        return 0;
    }
    

    后记

    这题只用了简单的线段树,却涉及到了网络流的知识,可以说是非常NOIP了。

  • 相关阅读:
    Linux查看系统版本信息和设置远程终端登录
    Linux环境下安装JDK
    Windows10修改Tomcat服务端口和一台机器部署多个Tomcat
    [转]C#反射-Assembly.Load、LoadFrom与LoadFile进阶
    【转】C# lock的使用
    一个简单的C++程序及说明
    插入排序
    堆排序
    选择排序
    快速排序
  • 原文地址:https://www.cnblogs.com/PPLPPL/p/15367126.html
Copyright © 2011-2022 走看看