【JZOJ 5419】【NOIP2017提高A组集训10.24】筹备计划

本文介绍了一个用于确定校庆最佳举办地点的算法。该算法通过计算每个潜在位置与参加学生位置之间的距离和,来找到距离和最小且最左侧的可行位置。考虑到某些位置可能因特殊情况而不可用,算法还需要灵活调整,确保能找到最优解。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Description

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

Solution

考虑一个位置成为最有的情况是什么:它往左移不会优,右移不会优,
那么也就是(sum为a的前缀和)
sumi>=Allsumi ,
Allsumi1>=sumi1
移项:
2sumi>=All ,
2sumi1<=All
所以找到最左的一个位置i,使得 2sumi>=All 即可,

那加入有些位置不能选怎么做?

显然,加入这个限制后的答案,一定是不加限制的最优位置的尽量左边或尽量右边,
那就先找出不加限制的最优位置,
再找出这个位置的左边第一个个可以选的,右边第一个可以选的,看看哪个优一点,

复杂度: O(nlog(n)2)
用线段树找不加限制的最优位置可以优化成: O(nlog(n))

Code

#include <cstdio>
#include <algorithm>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define fod(i,a,b) for(int i=a;i>=b;i--)
#define min(q,w) ((q)>(w)?(w):(q))
#define max(q,w) ((q)<(w)?(w):(q))
#define NX(q) ((q)&(-(q)))
using namespace std;
typedef long long LL;
const int N=200500;
int read(int &n)
{
    char ch=' ';int q=0,w=1;
    for(;(ch!='-')&&((ch<'0')||(ch>'9'));ch=getchar());
    if(ch=='-')w=-1,ch=getchar();
    for(;ch>='0' && ch<='9';ch=getchar())q=q*10+ch-48;n=q*w;return n;
}
int m,n,ans;
LL ans1;
LL a[N];
bool z[N];
LL ALLl,ALLr,ALLn;
LL sum[3][N];
struct qqww
{
    int v,la;
}b[N*4];
void ADD(int q,LL w,int E){for(;q<=n;q+=NX(q))sum[E][q]+=w;}
void JIA(int i,LL w){ALLn+=w,ALLl+=w*(LL)i,ALLr+=w*(LL)(n-i);ADD(i,w,0),ADD(i,w*(LL)i,1),ADD(i,w*(LL)(n-i),2);}
LL Gsum(int q,int E)
{
    LL ans=0;if(q>n)q=n;
    for(;q>0;q-=NX(q))ans+=sum[E][q];
    return ans;
}
void doit(int l,int r,int e)
{
    if(!b[e].la)return;
    b[e].v=(b[e].la-1)*(r-l+1);
    if(l!=r)b[e*2].la=b[e*2+1].la=b[e].la;
    b[e].la=0;
}
void change(int l,int r,int e,int l1,int r1,int l2)
{
    int t=(l+r)>>1;if(l!=r)doit(l,t,e*2),doit(t+1,r,e*2+1);
    if(l1<=l&&r<=r1){b[e].la=l2,doit(l,r,e);return;}
    if(r1<=t)change(l,t,e*2,l1,r1,l2);
    else if(t<l1)change(t+1,r,e*2+1,l1,r1,l2);
    else change(l,t,e*2,l1,t,l2),change(t+1,r,e*2+1,t+1,r1,l2);
    b[e].v=b[e*2].v+b[e*2+1].v;
}
int findl(int l,int r,int e,int r1)
{
    if(b[e].v==r-l+1)return -1;
    if(l==r)return l;
    int t=(l+r)>>1;if(l!=r)doit(l,t,e*2),doit(t+1,r,e*2+1);
    if(r1<=t)findl(l,t,e*2,r1);
    else 
    {
        int ans=findl(t+1,r,e*2+1,r1);
        return ans!=-1?ans:findl(l,t,e*2,r1);
    }
}
int findr(int l,int r,int e,int l1)
{
    if(b[e].v==r-l+1)return -1;
    if(l==r)return l;
    int t=(l+r)>>1;if(l!=r)doit(l,t,e*2),doit(t+1,r,e*2+1);
    if(t<l1)findr(t+1,r,e*2+1,l1);
    else 
    {
        int ans=findr(l,t,e*2,l1);
        return ans!=-1?ans:findr(t+1,r,e*2+1,l1);
    }
}
int EF(int l,int r)
{
    for(;l<r;)
    {
        int mid=(l+r)>>1;
        if(2LL*Gsum(mid,0)>=ALLn)r=mid;
        else l=mid+1;
    }
    return r;
}
LL JS(LL q)
{
    LL t=Gsum(q,0);
    LL ans=Gsum(q,2)-t*(LL)(n-q);
    ans+=ALLl-Gsum(q,1)-(LL)(ALLn-t)*(q);
    return ans;
}
int main()
{
    freopen("position.in","r",stdin);
    freopen("position.out","w",stdout);
    int q,w,e;
    read(n),read(m);
    fo(i,1,n)JIA(i,read(q));
    LL t;
    ans=EF(1,n);
    fo(I,1,m)
    {
        read(e);read(q),read(w);
        if(e==1)JIA(q,w);
        else if(e==2)JIA(q,-w);
        else if(e==3)change(1,n,1,q,w,1);
        else change(1,n,1,q,w,2);
        ans=EF(1,n);
        q=findl(1,n,1,ans);
        w=findr(1,n,1,ans);
        if(q==w&&q==-1){printf("-1\n");continue;}
        if(q==-1){printf("%d\n",w);continue;}
        if(w==-1){printf("%d\n",q);continue;}
        if(JS(w)<JS(q))printf("%d\n",w);
        else printf("%d\n",q);
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值