[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在线段树上查找。
我们发现,如果最优答案不可用,那么答案一定在它左边第一个可用点和右边第一个可用点的较小值,可以用上述维护的东西计算出。
代码:
设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; }
相关文章推荐
- [2017纪中10-24]方阵 二维ST表
- 【线性规划与网络流24题 10】餐巾计划
- [2017纪中10-24]合影 树型DP+组合数学
- [2017纪中10-25]天才绅士少女助手克里斯蒂娜 线段树
- 【线性规划与网络流24题 10】餐巾计划
- 【例题】【费用流(影响未来问题 )】NKOJ1945 【线性规划与网络流24题 10】餐巾计划
- 线性规划与网络流24题 10餐巾计划问题
- 线性规划与网络流24题 10餐巾计划问题
- 【网络流24题-2】太空飞行计划
- 10 12 24 忧心
- 2015_10_24
- [2017纪中10-23]公交运输 DP+斜率(玄学)优化
- [2017纪中10-28]三元组 贪心+排序/优先队列
- 我喜欢这样的老大[10-24] 推荐
- 2015.03.12,外语,读书笔记-《Word Power Made Easy》 10 “如何讨论交谈习惯”学习笔记 SESSION 24
- 近期学习计划及目标预期-2015-10-0…
- 4个10以内的数字,通过四则运算得到24
- 【BZOJ3207】花神的嘲讽计划Ⅰ hash+可持久化线段树
- 10-24
- 从零开始编写自己的C#框架(10)——项目实施计划与甘特图