JZOJ5419. 【NOIP2017提高A组集训10.24】筹备计划

30 篇文章 0 订阅
18 篇文章 0 订阅

Description

题目背景
热烈庆祝北京师范大学附属实验中学成立100周年!
问题描述
校庆筹备组的老师们正在寻找合适的地方来举办校庆庆典。
学生们的位置和可以举办庆典的位置在x轴的正半轴取值在[1,n]的整数位置上。
老师们选择的地点是会根据参加典礼的学生位置来决定的,具体来说:定义一个位置的距离和为该位置到所有参加学生的距离之和。如果一个位置的距离和最小,且它比所有和它距离和相等的位置的位置更靠左,则老师们会选择这个位置。
开始时,所有的位置都可以举办庆典。但很可惜的是,并不是所有的位置都能举办庆典,有些奇怪的事件会使[l,r]这段区间不能举办庆典,不过有时也会使[l,r]可以重新举办庆典(并不保证[l,r]之前的每个位置都不能举办庆典)。
有时一些学生会因为某些原因不能参加庆典,有时一些学生会主动报名参加庆典。
作为一名合格的老师,你需要求出每个事件发生后庆典应该选择的位置,如果没有合法位置,请输出-1。

Input

第一行包含两个整数n,q,表示坐标的范围和发生事件的个数。
第二行包含n个整数,第i个整数ai表示在初始时刻每个位置上的学生数量。
接下来q行每行先有一个整数type。
若type=1,接下来有两个整数x,k,表示在x位置增加k名学生。
若type=2,接下来有两个整数x,k,表示在x位置减少k名学生,保证x位置一定存在至少k名学生。
若type=3,接下来有两个整数l,r,表示在[l,r]这段区间可以重新举办庆典。
若type=4,接下来有两个整数l,r,表示在[l,r]这段区间不再能举办庆典。

Output

输出总共q行,第i行的数为第i个事件发生后的答案。

Sample Input

5 4
1 0 1 0 0
1 4 1
2 3 1
4 1 3
3 2 3

Sample Output

3
1
4
2

样例说明
总共5个位置可以选择
第1个事件发生,新增加一名学生在4号位置。
第2个事件发生以前,学生分别在1,3,4位置,可以证明,在3的位置距离和最小且它是最靠左的那一个。
第2个事件发生,减少一名在3位置的学生。
第3个事件发生以前,学生分别在1,4位置,可以证明,在1的位置距离和最小且它是最靠左的那一个。
第3个事件发生,1到3号位置不能举办庆典。
第4个事件发生以前,学生分别在1,4位置,且1到3号位置不能举办庆典,可以证明,在4的位置距离和最小且它是最靠左的那一个。
第4个事件发生,2到3号位置能重新举办庆典。
最后,学生分别在1,4位置,且1号位置不能举办庆典,可以证明,在2的位置距离和最小且它是最靠左的那一个。

Data Constraint

对于20%的数据满足,n ≤ 200, q ≤ 200
对于50%的数据满足,n ≤ 2000, q ≤ 2000
对于另30%的数据满足,不存在type = 3和type = 4的操作。
对于100%的数据满足,1 ≤ n ≤ 2 ∗ 10^5, 1 ≤ q ≤ 2 ∗ 10^5, 1 ≤ k ≤ 2 ∗ 10^5, 0 ≤ ai ≤ 2 ∗ 10^5。

题解

假设不存在type = 3和type = 4的操作,也就是说所有地方都可以举行校庆,
这样最优的位置一定是中位数的位置。
如果有了type = 3和type = 4的操作,也就是某些地方是不可以举行校庆的,
最优的位置就不一定是中位数的位置,因为这个位置可能不能举行校庆。
那么只能另外寻找位置来举行校庆。

通过感性的理解可以发现,最优的位置一定是左边或者右边距离中位数位置最近的那两个位置中更优的一个。

既然知道了这个,就考虑用线段树维护一些什么。
1、维护这个区间里面有多少个位置可以举办校庆。
2、维护一个人数的前缀和。
3、为了方便统计在某个位置举行校庆的距离和,还需要维护 aii ai(ni+1) 的前缀和,这个也可以用树状数组来维护。

每次寻找最优位置,
先在线段树上二分,得到中位数的位置,注意这里的中位数位置可能是一个区间。
如果可以在这个区间里面举行校庆,这就一定是最优的。
如果不行,就在线段树里面找到距离左端点最近且可以举办校庆的位置,以及距离右端点最近且可以举办校庆的位置。
通过维护的第三个东西,分别计算出它们的距离和,然后看看哪一个更优。

code

#include<queue>
#include<cstdio>
#include<iostream>
#include<algorithm>
#include <cstring>
#include <string.h>
#include <cmath>
#include <math.h>
#define ll long long
#define N 2000003
#define db double
#define P putchar
#define G getchar
#define mo 998244353
using namespace std;
char ch;
void read(ll &n)
{
    n=0;
    ch=G();
    while((ch<'0' || ch>'9') && ch!='-')ch=G();
    ll w=1;
    if(ch=='-')w=-1,ch=G();
    while('0'<=ch && ch<='9')n=(n<<3)+(n<<1)+ch-'0',ch=G();
    n*=w;
}

int max(int a,int b){return a>b?a:b;}
int min(int a,int b){return a<b?a:b;}
ll abs(ll x){return x<0?-x:x;}
ll sqr(ll x){return x*x;}
void write(ll x){if(x>9) write(x/10);P(x%10+'0');}
void writeln(ll x){write(x);P('\n');}

ll n,q,f[2][N],ans,w,x,y,l,r,opl,opr,opx,ops,a[N*4];
int t[N*4],lazy[N*4],x1,x2;

void find1(int x,int l,int r)
{
    if(lazy[x]==1)//重新举办校庆 
    {
        lazy[x<<1]=lazy[(x<<1)+1]=1;
        lazy[x]=0;int m=(l+r)>>1; 
        t[x<<1]=m-l+1;t[(x<<1)+1]=r-m;
    }
    if(lazy[x]==2)//不能举办校庆 
    {
        lazy[x<<1]=lazy[(x<<1)+1]=2;
        lazy[x]=t[x<<1]=t[(x<<1)+1]=0;
        return;
    }
    if(l==r){if(t[x]>0)ops=min(ops,l);return;}
    int m=(l+r)>>1;
    if(t[x<<1]>0)find1(x<<1,l,m);
        else find1((x<<1)+1,m+1,r);
}

void find2(int x,int l,int r)
{
    if(lazy[x]==1)//重新举办校庆 
    {
        lazy[x<<1]=lazy[(x<<1)+1]=1;
        lazy[x]=0;int m=(l+r)>>1; 
        t[x<<1]=m-l+1;t[(x<<1)+1]=r-m;
    }
    if(lazy[x]==2)//不能举办校庆 
    {
        lazy[x<<1]=lazy[(x<<1)+1]=2;
        lazy[x]=t[x<<1]=t[(x<<1)+1]=0;
        return;
    }
    if(l==r){if(t[x]>0)ops=max(ops,l);return;}
    int m=(l+r)>>1;
    if(t[(x<<1)+1]>0)find2((x<<1)+1,m+1,r);
        else find2(x<<1,l,m);
}

void work(int x,int l,int r)
{
    if(lazy[x]==1)//重新举办校庆 
    {
        lazy[x<<1]=lazy[(x<<1)+1]=1;
        lazy[x]=0;int m=(l+r)>>1; 
        t[x<<1]=m-l+1;t[(x<<1)+1]=r-m;
    }
    if(lazy[x]==2)//不能举办校庆 
    {
        lazy[x<<1]=lazy[(x<<1)+1]=2;
        lazy[x]=t[x<<1]=t[(x<<1)+1]=0;
    }
    if(opl<=l && r<=opr)
    {
        if(opx==1)a[x]+=ops;
        if(opx==2)a[x]-=ops;
        if(opx==3)t[x]=r-l+1,lazy[x]=1;//重新举办校庆
        if(opx==4)
            t[x]=0,lazy[x]=2;//不能举办校庆 
        if(opx==5)find1(x,l,r);
        if(opx==6)find2(x,l,r);
        if(opx==7)ops+=a[x];
        return;
    }
    int m=(l+r)>>1;
    if(m>=opl)work(x<<1,l,m);
    if(m<opr)work((x<<1)+1,m+1,r);
    t[x]=t[x<<1]+t[(x<<1)+1];
    a[x]=a[x<<1]+a[(x<<1)+1];
}

void find(int x,int l,int r,ll s)
{
    if(l==r){x2=l;return;}
    int m=(l+r)>>1;
    if(a[x<<1]>=s)find(x<<1,l,m,s);
        else find((x<<1)+1,m+1,r,s-a[x<<1]);
}

int x_(int x){return x&(-x);}

void ins(int x,ll y,int z)
{
    for(int i=x;i<=n;i=i+x_(i))
        f[z][i]+=y;
}

ll get(int x,int y)
{
    ll s=0;
    for(int i=x;i;i=i-x_(i))
        s+=f[y][i];
    return s;
}

ll solve(int x)
{
    ll s=0;
    s=get(n,1)-get(n-x+1,1);
    opl=1;opr=x-1;ops=0;opx=7;
    if(opl<=opr)work(1,1,n);
    s-=ops*(n-x+1);

    s+=get(n,0)-get(x,0);
    opl=x+1;opr=n;ops=0;opx=7;
    if(opl<=opr)work(1,1,n);
    s-=ops*x;   

    return s;
}

int main()
{
    freopen("position.in","r",stdin);
    freopen("position.out","w",stdout);
    read(n);read(q);opx=1;
    for(int i=1;i<=n;i++)
        read(ops),opl=opr=i,work(1,1,n),ins(i,ops*i,0),ins(n-i+1,ops*(n-i+1),1);

    opx=3;opl=1;opr=n;work(1,1,n);
    find(1,1,n,(a[1]+1)/2);x1=x2;
    find(1,1,n,(a[1]+1)/2+1); 

    for(int k=1;k<=q;k++)
    {       
        read(opx);read(l);read(r);
        if(opx==1 || opx==2)opl=opr=l,ops=r;
            else opl=l,opr=r;
        work(1,1,n);
        if(opx==1)ins(l,r*l,0),ins(n-l+1,(n-l+1)*r,1);
        if(opx==2)ins(l,-r*l,0),ins(n-l+1,-(n-l+1)*r,1);; 

        if(opx==1 || opx==2)
        {
            find(1,1,n,(a[1]+1)/2);x1=x2;
            find(1,1,n,(a[1]+1)/2+1); 
        }

        ans=9223372036854775800;
        ops=2147483647;opl=x2;opr=n;opx=5;work(1,1,n);
        if(ops<=n)
        {
            x=solve(y=ops);
            if(x<ans)ans=x,w=y;
        }

        ops=-2147483647+x2;opl=1;opr=x1;opx=6;work(1,1,n);
        if(ops>0)
        {
            x=solve(y=ops);
            if(x<=ans)ans=x,w=y;
        }

        ops=2147483647;opl=x1;opr=x2;opx=5;work(1,1,n);
        if(ops<=n)w=ops;

        if(ans!=9223372036854775800)writeln(w);else P('-'),P('1'),P('\n');
    }
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值