jzoj6078 [GDOI2019模拟2019.3.22]魔法阵 树状数组套线段树

10 篇文章 0 订阅

Description


给定序列A[]和B[],记C[i]=max(C[i-1],A[i]),要求资瓷两个操作:

  1. 0 x y,改A[x]为y
  2. 1 x y,改B[x]为y

每次操作输出 ∏ i = 1 n m i n ( B i , C i ) \prod\limits_{i=1}^{n}{min(B_i,C_i)} i=1nmin(Bi,Ci)
n ≤ 1 0 5 n\le 10^5 n105,保证 y ≥ A x y\ge A_x yAx

Solution


很套路的题目,细节比较多而且很繁琐。感觉自己码力不太行了

别人都写3k我写5k系列

可以发现A[i]是不减的,那么我们就可以算出它作为前缀最大值的最长区间[L,R],于是这一整段区间的C都是一样的了
观察可以发现,这些最大值的区间互不相交,且随着我们的操作,它们的数量是不增的。那么我们就可以暴力把[L,R]之间的区间贡献都除掉,然后乘上[L,R]的答案

考虑一整段区间的B[]和某个固定的C取min后乘积,显然比C小的都是它们本身,比C大的我们只需要统计多少个就能快速幂了。这就是一个带修改二维数点问题,上树套树就ojbk

预处理的话,单点修改+区间最大值可以线段树,注意到这实际上是前缀最大值因此可以树状数组。记录某个位置最右边界R[i]可以区间修改+单点查询线段树。我比较喜欢线段树所以写了很多棵线段树(滑稽

Code


#include <stdio.h>
#include <string.h>
#include <algorithm>
#define rep(i,st,ed) for (int i=st;i<=ed;++i)
#define lowbit(x) (x&-x)

typedef long long LL;
const int INF=0x3f3f3f3f;
const int MOD=1000000007;
const int N=200005;

struct treeNode {
	int l,r;
	LL prod,cnt;
} t[N*175];

int rt[N],max[N<<2],R[N<<2],RR[N<<2],n,m,tot;
LL a[N],b[N],w[N],inv,ans;

int read() {
	int x=0,v=1; char ch=getchar();
	for (;ch<'0'||ch>'9';v=(ch=='-')?(-1):(v),ch=getchar());
	for (;ch<='9'&&ch>='0';x=x*10+ch-'0',ch=getchar());
	return x*v;
}

LL ksm(LL x,LL dep) {
	LL res=1;
	for (;dep;dep>>=1) {
		(dep&1)?(res=res*x%MOD):0;
		x=x*x%MOD;
	}
	return res;
}

void modify(int &now,int tl,int tr,LL x,int v) {
	if (!now) t[now=++tot].prod=1;
	t[now].cnt+=v;
	if (v==-1) t[now].prod=t[now].prod*inv%MOD;
	else t[now].prod=t[now].prod*x%MOD;
	if (tl==tr) return ;
	int mid=(tl+tr)>>1;
	if (x<=mid) modify(t[now].l,tl,mid,x,v);
	else modify(t[now].r,mid+1,tr,x,v);
}

void ins(int x,int pos,int v) {
	for (;x<=n;x+=lowbit(x)) modify(rt[x],1,INF,pos,v);
}

int count(int now,int tl,int tr,int l,int r) {
	if (!now) return 0;
	if (tl>=l&&tr<=r) return t[now].cnt;
	int mid=(tl+tr)>>1,res=0;
	if (l<=mid) res+=count(t[now].l,tl,mid,l,r);
	if (mid+1<=r) res+=count(t[now].r,mid+1,tr,l,r);
	return res;
}

LL mul(int now,int tl,int tr,int l,int r) {
	if (!now) return 1;
	if (tl>=l&&tr<=r) return t[now].prod;
	int mid=(tl+tr)>>1; LL res=1;
	if (l<=mid) res=res*mul(t[now].l,tl,mid,l,r)%MOD;
	if (mid+1<=r) res=res*mul(t[now].r,mid+1,tr,l,r)%MOD;
	return res;
}

LL askc(int x,int l,int r) {
	LL res=0;
	for (;x;x-=lowbit(x)) res+=count(rt[x],1,INF,l,r);
	return res;
}

LL askm(int x,int l,int r) {
	LL res=1;
	for (;x;x-=lowbit(x)) res=res*mul(rt[x],1,INF,l,r)%MOD;
	return res;
}

int query_max(int now,int tl,int tr,int l,int r) {
	if (r<l) return 0;
	if (tl>=l&&tr<=r) return max[now];
	int mid=(tl+tr)>>1,qx=0,qy=0;
	if (l<=mid) qx=query_max(now<<1,tl,mid,l,r);
	if (mid+1<=r) qy=query_max(now<<1|1,mid+1,tr,l,r);
	return std:: max(qx,qy);
}

void change(int now,int tl,int tr,int x,int v) {
	if (tl==tr) return (void) (max[now]=v);
	int mid=(tl+tr)>>1;
	if (x<=mid) change(now<<1,tl,mid,x,v);
	else change(now<<1|1,mid+1,tr,x,v);
	max[now]=std:: max(max[now<<1],max[now<<1|1]);
}

void right(int now,int tl,int tr,int l,int r,int v) {
	if (tl>=l&&tr<=r) return (void) (R[now]=RR[now]=v);
	if (RR[now]) {
		RR[now<<1]=RR[now<<1|1]=RR[now];
		R[now<<1]=R[now<<1|1]=RR[now]; RR[now]=0;
	}
	int mid=(tl+tr)>>1;
	if (l<=mid) right(now<<1,tl,mid,l,r,v);
	if (mid+1<=r) right(now<<1|1,mid+1,tr,l,r,v);
}

int get_right(int now,int tl,int tr,int x) {
	if (tl==tr) return R[now];
	if (RR[now]) {
		RR[now<<1]=RR[now<<1|1]=RR[now];
		R[now<<1]=R[now<<1|1]=RR[now]; RR[now]=0;
	}
	int mid=(tl+tr)>>1;
	if (x<=mid) return get_right(now<<1,tl,mid,x);
	else return get_right(now<<1|1,mid+1,tr,x);
}

LL calc(int l,int r,LL c) {
	LL res1=askm(r,1,c);
	LL tmp=askm(l-1,1,c);
	tmp=ksm(tmp,MOD-2);
	res1=res1*tmp%MOD;
	LL res2=askc(r,c+1,INF)-askc(l-1,c+1,INF);
	res2=ksm(c,res2);
	return res1*res2%MOD;
}

void solve(int l,int r,int x,LL c) {
	int tl=1,tr=l,res=l,wjp=get_right(1,1,n,l);
	while (tl<=tr) {
		int mid=(tl+tr)>>1;
		if (get_right(1,1,n,mid)==wjp) tr=mid-1,res=mid;
		else tl=mid+1;
	}
	if (res<=l-1) {
		right(1,1,n,res,l-1,l-1);
		w[l-1]=w[wjp];
	}
	int now=l;
	while (now<=r) {
		int rig=get_right(1,1,n,now);
		rig=std:: min(r,rig);
		LL tmp=calc(now,rig,w[rig]);
		tmp=ksm(tmp,MOD-2);
		ans=ans*tmp%MOD;
		now=rig+1;
	}
	right(1,1,n,l,r,r); w[r]=c;
	ans=ans*calc(l,r,c)%MOD;
}

int main(void) {
	freopen("data.in","r",stdin);
	freopen("myp.out","w",stdout);
	n=read(),m=read();
	rep(i,1,n) a[i]=read();
	rep(i,1,n) b[i]=read();
	LL mx=0,last=0; ans=1;
	rep(i,1,n) {
		change(1,1,n,i,a[i]);
		if (a[i]>mx) {
			if (last) right(1,1,n,last,i-1,i-1);
			w[i-1]=mx;
			mx=a[i];
			last=i;
		}
		ans=ans*std:: min(mx,b[i])%MOD;
		ins(i,b[i],1);
	}
	right(1,1,n,last,n,n); w[n]=mx;
	for (;m--;) {
		int opt=read(),x=read(),y=read();
		if (opt==0) {
			int mm=query_max(1,1,n,1,x-1);
			if (mm<y) {
				int l=x,r=n,res=x;
				while (l<=r) {
					int mid=(l+r)>>1;
					if (query_max(1,1,n,x+1,mid)>=y) r=mid-1;
					else l=mid+1,res=mid;
				}
				solve(x,res,x,y);
			}
			change(1,1,n,x,y); a[x]=y;
		} else {
			int rr=get_right(1,1,n,x);
			LL tmp=std:: min(1LL*w[rr],b[x]);
			tmp=ksm(tmp,MOD-2);
			ans=ans*tmp%MOD;
			ans=ans*std:: min(1LL*y,1LL*w[rr])%MOD;
			inv=ksm(b[x],MOD-2);
			ins(x,b[x],-1);
			b[x]=y;
			ins(x,b[x],1);
		}
		printf("%lld\n", ans);
	}
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值