(Nowcoder) E.Find the median (线段树)

传送门

题意:给你n个区间,每次往你现有的数中加上l,l+1,.....,r-1,r这些数,求现在所有数的中位数

解:区间的范围很大,我们肯定需要离散化,解法也很直观,肯定是线段树维护吗,就维护区间出现点的个数,这样我们query的时候就可以根据数量一直走到叶子节点,求出答案,所以我们每个叶子节点就是代表一段小区间了。这里算是一个小技巧吧,我们把所有的区间都看成是左开右闭的,这样叶子节点的区间就不怕冲突了。比如给出区间[1,3],[2,7],我们将其看成[1,4),[2,8);经过我们离散化会得到 1,2,4,8 ,那我们线段树的叶子节点所代表的区间就是[1,2),[2,4),[4,8).

#include<bits/stdc++.h>
#define il inline
#define pb push_back
#define ms(_data,v) memset(_data,v,sizeof(_data))
#define sc(n) scanf("%d",&n)
#define SC(n,m) scanf("%d %d",&n,&m)
#define SZ(a) int((a).size())
#define rep(i,a,b) for(int i=a;i<=b;++i)
#define drep(i,a,b)	for(int i=a;i>=b;--i)
using namespace std;
typedef long long ll;
const ll inf=0x3f3f3f3f;
const double pi=acos(-1.0);
const double eps=1e-9;
const int maxn=4e5+5;
//il int Add(int x,int y) {return x+y>=mod?x+y-mod:x+y;}
//il int Mul(ll x,int y) {return x*y>=mod?x*y%mod:x*y;}
int X[maxn],Y[maxn],A1,A2,B1,B2,C1,C2,M1,M2,N;
int L[maxn],R[maxn],s[maxn<<2];
struct node{
	int l,r,lz;
	ll num;
	bool fg;
}sum[maxn<<3];
il void pushup(int rt){
	sum[rt].num=sum[rt<<1].num+sum[rt<<1|1].num;
}
il void pushdown(int rt){
	if(sum[rt].lz){
		sum[rt<<1].lz+=sum[rt].lz;
		sum[rt<<1|1].lz+=sum[rt].lz;
		sum[rt<<1].num+=1LL*sum[rt].lz*(sum[rt<<1].r-sum[rt<<1].l);
		sum[rt<<1|1].num+=1LL*sum[rt].lz*(sum[rt<<1|1].r-sum[rt<<1|1].l);
		sum[rt].lz=0;
	}
}
il void build(int l,int r,int rt){
	if(l==r){
		sum[rt].l=s[l],sum[rt].r=s[l+1];
		sum[rt].fg=1;
		return ;
	}
	int mid=(l+r)>>1;
	build(l,mid,rt<<1);
	build(mid+1,r,rt<<1|1);
	sum[rt].l=sum[rt<<1].l,sum[rt].r=sum[rt<<1|1].r;
}
il void update(int L,int R,int rt){
	if(L<=sum[rt].l && R>=sum[rt].r){
		sum[rt].num+=sum[rt].r-sum[rt].l;
		sum[rt].lz++;
		return ;
	}
	pushdown(rt);
	if(L<sum[rt<<1].r) update(L,R,rt<<1);
	if(R>sum[rt<<1|1].l) update(L,R,rt<<1|1);
	pushup(rt);
}
il int query(int rt,ll cnt){
	if(sum[rt].fg){
		ll tp=sum[rt].num/(sum[rt].r-sum[rt].l);
		if(cnt%tp==0) return sum[rt].l+(cnt/tp)-1;
		else return sum[rt].l+(cnt/tp);
	}
	pushdown(rt);
	if(cnt<=sum[rt<<1].num) return query(rt<<1,cnt);
	else return query(rt<<1|1,cnt-sum[rt<<1].num);
}
int main(){
	std::ios::sync_with_stdio(0);
	scanf("%d",&N);
	scanf("%d%d%d%d%d%d",&X[1],&X[2],&A1,&B1,&C1,&M1);
	scanf("%d%d%d%d%d%d",&Y[1],&Y[2],&A2,&B2,&C2,&M2);
	int tot=0; 
	for(int i=1;i<=N;++i){
		if(i>=3){
			X[i]=(1LL*A1*X[i-1]+1LL*B1*X[i-2]+C1)%M1;
			Y[i]=(1LL*A2*Y[i-1]+1LL*B2*Y[i-2]+C2)%M2;
		}
		L[i]=min(X[i],Y[i])+1;
		R[i]=max(X[i],Y[i])+2;//左闭右开 
		s[++tot]=L[i],s[++tot]=R[i]; 
	}
	sort(s+1,s+tot+1);
	tot=unique(s+1,s+tot+1)-(s+1);
	build(1,tot-1,1);
	ll all=0;
	for(int i=1;i<=N;++i){
		update(L[i],R[i],1);
		all+=R[i]-L[i];
		printf("%d\n",query(1,(all+1)/2));
	}
	return 0;
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值