【YbtOJ#20066】筹备计划
题目
题目链接:http://noip.ybtoj.com.cn/contest/90/problem/4
思路
这种题就硬缝合。。。
显然能造成贡献的是学生人数中位数左右的可以被选则的点。
维护一棵树状数组维护学生人数以及带权学生人数(也就是第 \(i\) 个位置的学生人数 \(\times i\))(下文称前者为树状数组 1,后者为树状数组 2),以及可以线段树维护可以被选中的区间。
先求出树状数组 1 全局的和,求出其中位数 \(sum\),再用树状数组二分求出中位数所在位置 \(p\)。
然后在线段树上用线段树二分求出 \(p\) 前后第一个可以被选择的位置,具体的先在线段树上求出 \(1\sim p\) 的和 \(cnt\),然后二分出第一个和为 \(cnt\) 和 \(cnt+1\) 的位置 \(pos1\) 和 \(pos2\),这两个位置就可能是答案。
然后我们求出 \(pos1\) 和 \(pos2\) 的答案并作比较。具体的可以把答案拆开,分成在 \(pos\) 左右两边的答案加起来,而这两边可以用两个树状数组计算出贡献。
然后取答案较小的那边为答案。
时间复杂度 \(O(n\log n)\)。
吐槽一下这道题还卡两棵线段树,考场写两棵线段树还没有 XJQ 大爷的 \(O(n\log^2 n)\) 分数高。。。
代码
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=200010,M=(1<<18);
int n,m,a[N];
struct BIT
{
ll c[M+10];
void add(int x,ll v)
{
for (int i=x;i<=M;i+=i&-i)
c[i]+=v;
}
ll query(int x)
{
ll ans=0;
for (int i=x;i;i-=i&-i)
ans+=c[i];
return ans;
}
int binary(ll k)
{
int p=0;
for (int i=(M>>1);i>=1;i>>=1)
if (k>c[p+i]) k-=c[p+i],p+=i;
return p+1;
}
}bit1,bit2;
struct SegTree
{
int l[N*4],r[N*4],sum[N*4],lazy[N*4];
SegTree() { memset(lazy,-1,sizeof(lazy)); }
void pushup(int x)
{
sum[x]=sum[x*2]+sum[x*2+1];
}
void pushdown(int x)
{
if (lazy[x]!=-1)
{
lazy[x*2]=lazy[x*2+1]=lazy[x];
sum[x*2]=lazy[x]*(r[x*2]-l[x*2]+1);
sum[x*2+1]=lazy[x]*(r[x*2+1]-l[x*2+1]+1);
lazy[x]=-1;
}
}
void build(int x,int ql,int qr)
{
l[x]=ql; r[x]=qr;
if (ql==qr)
{
sum[x]=1;
return;
}
int mid=(ql+qr)>>1;
build(x*2,ql,mid); build(x*2+1,mid+1,qr);
pushup(x);
}
void update(int x,int ql,int qr,int v)
{
if (l[x]==ql && r[x]==qr)
{
sum[x]=v*(r[x]-l[x]+1);
lazy[x]=v;
return;
}
pushdown(x);
int mid=(l[x]+r[x])>>1;
if (qr<=mid) update(x*2,ql,qr,v);
else if (ql>mid) update(x*2+1,ql,qr,v);
else update(x*2,ql,mid,v),update(x*2+1,mid+1,qr,v);
pushup(x);
}
int query(int x,int ql,int qr)
{
if (l[x]==ql && r[x]==qr)
return sum[x];
pushdown(x);
int mid=(l[x]+r[x])>>1;
if (qr<=mid) return query(x*2,ql,qr);
if (ql>mid) return query(x*2+1,ql,qr);
return query(x*2,ql,mid)+query(x*2+1,mid+1,qr);
}
int binary(int x,int k)
{
if (l[x]==r[x])
{
if (sum[x]) return l[x];
else return -1;
}
pushdown(x);
if (sum[x*2]>=k) return binary(x*2,k);
else return binary(x*2+1,k-sum[x*2]);
}
}seg;
int main()
{
freopen("position.in","r",stdin);
freopen("position.out","w",stdout);
scanf("%d%d",&n,&m);
for (int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
bit1.add(i,a[i]); bit2.add(i,1LL*a[i]*i);
}
seg.build(1,1,n);
while (m--)
{
int type,x,y;
scanf("%d%d%d",&type,&x,&y);
if (type==1) bit1.add(x,y),bit2.add(x,1LL*y*x),a[x]+=y;
if (type==2) bit1.add(x,-y),bit2.add(x,-1LL*y*x),a[x]-=y;
if (type==3) seg.update(1,x,y,1);
if (type==4) seg.update(1,x,y,0);
ll sum=(bit1.query(n)+1)>>1;
int p=bit1.binary(sum);
int cnt=seg.query(1,1,p);
int pos1=seg.binary(1,cnt),pos2=seg.binary(1,cnt+1);
if (pos1==-1) pos1=pos2;
if (pos2==-1) pos2=pos1;
if (pos1<0 && pos2<0) { printf("-1\n"); continue; }
ll s1=bit2.query(n)-bit2.query(pos1-1)-(bit1.query(n)-bit1.query(pos1-1))*pos1+bit1.query(pos1)*pos1-bit2.query(pos1);
ll s2=bit2.query(n)-bit2.query(pos2-1)-(bit1.query(n)-bit1.query(pos2-1))*pos2+bit1.query(pos2)*pos2-bit2.query(pos2);
if (s1<=s2) printf("%d\n",pos1);
else printf("%d\n",pos2);
}
return 0;
}