【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;
}
posted @ 2020-10-12 17:07  stoorz  阅读(158)  评论(0编辑  收藏  举报