gym102391 2019-2020 XX Open Cup, Grand Prix of Korea F Hilbert‘s Hotel

题目链接

题目:

请看原题题面

题解:

可以发现每个组所占的房间的间隔永远是一样的,所以房间位置就是一个等差数列,我们维护每个组的最左端的房间位置 s t a r t start start和间隔 s t e p step step,具体的是用两棵线段树分别维护 s t a r t start start s t e p step step,那么操作1和操作2就变成了带有乘法和加法标记的线段树的操作了。对于操作3另外考虑,我们把连续的来有限个客人的情况所在一起,那么对于操作3的 x x x可以通过回溯来客人的影响得到答案,来无限个客人会使 x x x除以2,对于有限个客人由于连续的情况我们缩在一起考虑,所以要么全部回溯,要么答案就在这个连续段内,那么我们预处理出这一段的前缀和,二分即可,所以操作3的复杂度是 l o g log log的。

复杂度: O ( q l o g x ) O(qlogx) O(qlogx)
代码:
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<vector>
#include<queue>
#include<stack>
#include<map>
#include<set>
#include<string>
#include<bitset>
#include<sstream>
#include<ctime>
//#include<chrono>
//#include<random>
//#include<unordered_map>
using namespace std;

#define ll long long
#define ls o<<1
#define rs o<<1|1
#define pii pair<int,ll>
#define fi first
#define se second
#define pb push_back
#define mp make_pair
#define sz(x) (int)(x).size()
#define all(x) (x).begin(),(x).end()
const double pi=acos(-1.0);
const double eps=1e-6;
const int mod=1e9+7;
const int INF=0x3f3f3f3f;
const int maxn=3e5+5;
ll read(){
	ll x=0,f=1;
	char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
	while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
	return x*f;
}
int q,cnt=0,tp,lst,tot,fp,ers[maxn];
vector<pii>fi[maxn];
int inf[maxn];
struct SegTree{
	struct node{
		int l,r;
		ll v,add,mul;
	}t[maxn<<2];
	void build(int o,int l,int r){
		t[o].l=l;t[o].r=r;t[o].add=0;t[o].mul=1;t[o].v=0;
		if(l==r)return;
		int m=(l+r)>>1;
		build(ls,l,m);
		build(rs,m+1,r);
	}
	void pushup(int o){
		t[o].v=(t[ls].v+t[rs].v)%mod;
	}
	void pushdown(int o){
		if(t[o].mul!=1){
			t[ls].add=t[ls].add*t[o].mul%mod;
			t[ls].mul=t[ls].mul*t[o].mul%mod;
			t[ls].v=t[ls].v*t[o].mul%mod;
			t[rs].add=t[rs].add*t[o].mul%mod;
			t[rs].mul=t[rs].mul*t[o].mul%mod;
			t[rs].v=t[rs].v*t[o].mul%mod;
			t[o].mul=1;
		}
		if(t[o].add){
			t[ls].add=(t[ls].add+t[o].add)%mod;
			t[ls].v=(t[ls].v+t[o].add*(t[ls].r-t[ls].l+1)%mod)%mod;
			t[rs].add=(t[rs].add+t[o].add)%mod;
			t[rs].v=(t[rs].v+t[o].add*(t[rs].r-t[rs].l+1)%mod)%mod;
			t[o].add=0;
		}
	}
	void upd_add(int o,int ql,int qr,int k){
		if(ql<=t[o].l&&t[o].r<=qr){
			t[o].add=(t[o].add+k)%mod;
			t[o].v=(t[o].v+k*(t[o].r-t[o].l+1)%mod)%mod;
			return;
		}
		pushdown(o);
		int m=(t[o].l+t[o].r)>>1;
		if(ql<=m)upd_add(ls,ql,qr,k);
		if(qr>m)upd_add(rs,ql,qr,k);
		pushup(o);
	}
	void upd_mul(int o,int ql,int qr){
		if(ql<=t[o].l&&t[o].r<=qr){
			t[o].mul=t[o].mul*2%mod;
			t[o].add=t[o].add*2%mod;
			t[o].v=t[o].v*2%mod;
			return;
		}
		pushdown(o);
		int m=(t[o].l+t[o].r)>>1;
		if(ql<=m)upd_mul(ls,ql,qr);
		if(qr>m)upd_mul(rs,ql,qr);
		pushup(o);
	}
	void upd(int o,int p,int v){
		if(t[o].l==t[o].r){
			t[o].v=v;
			return;
		}
		pushdown(o);
		int m=(t[o].l+t[o].r)>>1;
		if(p<=m)upd(ls,p,v);
		else upd(rs,p,v);
		pushup(o);
	}
	ll qry(int o,int p){
		if(t[o].l==t[o].r){
			return t[o].v;
		}
		pushdown(o);
		int m=(t[o].l+t[o].r)>>1;
		if(p<=m)return qry(ls,p);
		else return qry(rs,p);
	}
}start,step;
int main(void){
	// freopen("in.txt","r",stdin);
	q=read();
	cnt=1;tp=0;tot=0;fp=0;
	start.build(1,1,q);
	step.build(1,1,q);
	start.upd(1,1,0);
	step.upd(1,1,1);
	int op,k,g,x;
	inf[++tp]=1;
	lst=0;
	int et=0;
	while(q--){
		op=read();
		if(op==1){
			k=read();
			if(k){
				start.upd_add(1,1,cnt,k);
				++cnt;
				start.upd(1,cnt,0);
				step.upd(1,cnt,1);
				if(lst){
					fi[fp].pb(mp(k,fi[fp].back().se+k));
				}
				else{
					++fp;
					fi[fp].pb(mp(k,k));
				}
				lst=1;
				et=0;
			}
			else{
				start.upd_mul(1,1,cnt);
				step.upd_mul(1,1,cnt);
				++cnt;
				start.upd(1,cnt,1);
				step.upd(1,cnt,2);
				inf[++tp]=cnt;
				lst=0;
				if(!et){
					ers[tp]=cnt;
					et=cnt;
				}
				else{
					ers[tp]=et;
				}
			}
		}
		else if(op==2){
			g=read();x=read();
			++g;
			ll s=start.qry(1,g);
			ll p=step.qry(1,g);
			printf("%lld\n",(s+p*(x-1)%mod)%mod);
		}
		else{
			ll x=read();
			int ttp=tp,ffp=fp,flag,cur=cnt+1;
			if(lst){
				flag=1;
			}
			else{
				flag=0;
			}
			while(1){
				if(flag){
					if(x>=fi[ffp].back().se){
						x-=fi[ffp].back().se;
						--ffp;
						flag=0;
					}
					else{
						int l=0,r=sz(fi[ffp])-1,ans;
						ll sum=fi[ffp].back().se;
						while(l<=r){
							int m=(l+r)>>1;
							if(sum-fi[ffp][m].se<=x){
								ans=m;
								r=m-1;
							}
							else{
								l=m+1;
							}
						}
						ans=cur-(sz(fi[ffp])-ans);
						printf("%d\n",ans-1);
						break;
					}
				}
				else{
					if(ttp==1){
						puts("0");
						break;
					}
					if(x==0){
						printf("%d\n",max(ers[ttp]-1,1)-1);
						break;
					}
					if(x%2){
						printf("%d\n",inf[ttp]-1);
						break;
					}
					else{
						cur=inf[ttp];
						x/=2;
						--ttp;
						if(inf[ttp]+1!=inf[ttp+1]){
							flag=1;
						}
					}
				}
			}
		}
	}
	return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值