您的位置:首页 > 其它

[2017纪中10-24]筹备计划 线段树

2017-10-24 16:41 405 查看
题面

设a[i]为i点学生个数。

考虑用线段树维护这么几个东西:sum0(区间内a[i]和),sum1(区间内a[i]*i和),d0(区间是否全部不可用),d1(区间是否全部可用)。

sum0,sum1只有单点修改,d0,d1只有区间覆盖。

那么我们可以O(logn)计算下列东西:

不考虑可用不可用的最优答案:就是中位数,通过sum0就可以搞定。

某一点X的答案:sum0[1,x-1] * x-sum1[1,x-1]+sum1[x+1,n]-sum0[x+1,n] * x。

某一点往左/往右第一个可用点:通过d0,d1在线段树上查找。

我们发现,如果最优答案不可用,那么答案一定在它左边第一个可用点和右边第一个可用点的较小值,可以用上述维护的东西计算出。

代码:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#define ll long long
using namespace std;
const int maxn=200010;
int n,q;
ll tot=0,a[maxn];
int read()
{
int x=0;
char ch=getchar();
while(ch<'0'||ch>'9') ch=getchar();
while(ch>='0'&&ch<='9') {x=x*10+ch-'0';ch=getchar();}
return x;
}
struct tree
{
int l,r,b;
ll sum[2];
bool d0,d1;
tree *ls,*rs;
tree()
{
ls=rs=NULL;
l=r=sum[0]=sum[1]=0;
d0=0;d1=1;b=-1;
}
void cal()
{
if(b==0) {d0=1;d1=0;}
if(b==1) {d0=0;d1=1;}
}
void pushdown()
{
if(b>=0)
{
ls->b=b;rs->b=b;
ls->cal();rs->cal();
b=-1;
}
}
void update()
{
for(int i=0;i<=1;i++)
sum[i]=ls->sum[i]+rs->sum[i];
d0=ls->d0&&rs->d0;
d1=ls->d1&&rs->d1;
}
void build(int lx,int rx)
{
l=lx;r=rx;
if(l==r) {sum[0]=a[l];sum[1]=sum[0]*l;return ;}
int mid=(l+r)>>1;
(ls=new tree)->build(lx,mid);
(rs=new tree)->build(mid+1,rx);
update();
}
void add(int pl,int c)
{
if(l==r) {sum[0]+=c;sum[1]+=(ll)c*pl;return ;}
pushdown();
int mid=(l+r)>>1;
if(pl<=mid) ls->add(pl,c);
else rs->add(pl,c);
update();
}
void mdf(int lx,int rx,int f)
{
if(lx==l&&rx==r) {b=f;cal();return ;}
pushdown();
int mid=(l+r)>>1;
if(rx<=mid) ls->mdf(lx,rx,f);
else if(lx>mid) rs->mdf(lx,rx,f);
else {ls->mdf(lx,mid,f);rs->mdf(mid+1,rx,f);}
update();
}
int qz(ll k)
{
if(l==r) {return l;}
pushdown();
ll t=ls->sum[0];
if(k<=t) return ls->qz(k);
else return rs->qz(k-t);
}
ll qsum(int lx,int rx,int f)
{
if(lx>rx) return 0;
if(lx==l&&rx==r) return sum[f];
pushdown();
int mid=(l+r)>>1;
if(rx<=mid) return ls->qsum(lx,rx,f);
else if(lx>mid) return rs->qsum(lx,rx,f);
else return ls->qsum(lx,mid,f)+rs->qsum(mid+1,rx,f);
}
int findl(int p)
{
if(d0) return -1;
if(l==r) return l;
pushdown();
int mid=(l+r)>>1,tmp=-1;
if(p>mid) tmp=rs->findl(p);
if(tmp==-1) tmp=ls->findl(min(p,mid));
return tmp;
}
int findr(int p)
{
if(d0) return -1;
if(l==r) return l;
pushdown();
int mid=(l+r)>>1,tmp=-1;
if(p<=mid) tmp=ls->findr(p);
if(tmp==-1) tmp=rs->findr(max(p,mid+1));
return tmp;
}
}*xtr;
ll calc(int x)
{
if(x==-1) return 1e18;
return xtr->qsum(1,x-1,0)*x-xtr->qsum(1,x-1,1)+xtr->qsum(x+1,n,1)-xtr->qsum(x+1,n,0)*x;
}
int main()
{
n=read();q=read();
for(int i=1;i<=n;i++)
{
a[i]=read();
tot+=a[i];
}
(xtr=new tree)->build(1,n);
bool pd=1;
for(int i=1;i<=q;i++)
{
int opt=read(),x=read(),y=read();
if(opt==1){xtr->add(x,y);tot+=y;a[x]+=y;}
if(opt==2){xtr->add(x,-y);tot-=y;a[x]-=y;}
if(opt==3){xtr->mdf(x,y,1);}
if(opt==4){xtr->mdf(x,y,0);}
if(opt==3||opt==4) pd=0;
int md=xtr->qz((tot+1)/2),lb,rb;
if(pd) {printf("%d\n",md);continue;}
lb=xtr->findl(md);rb=xtr->findr(md);
if(lb==md||rb==md) {printf("%d\n",md);continue;}
if(lb==-1&&rb==-1) {puts("-1");continue;}
if(calc(lb)<=calc(rb)) printf("%d\n",lb);
else printf("%d\n",rb);
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: