ICPC2019徐州 Yuuki and a problem 树套树

https://nanti.jisuanke.com/t/42547

学习自https://www.cnblogs.com/zxytxdy/p/12375629.html

顺便复习了一下树状数组套主席树怎么写,上一次写好像是17年= =

要维护区间有哪些值,所以主席树搞搞,然后又因为要变化,显然就是套个树状数组在外面

然后这题关键是怎么求出区间所有值可组成的总和的mex

首先1不行的话,然后是2,然后由于12都行了,所以然后是3,再然后就是5,也就是说我们只要判断fibonacci数列上的数是否存在就行了,由于ai最大是2e5,所以暴力查询的次数并不会很多

复杂度是nlog^3 ,15s常数再大也够了,我的只在计蒜客上跑了1s多点

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;

const int maxl=2e5+10;
const int len=2e5;

int n,m,tot,cnt1,cnt2;
int a[maxl],rt[maxl*80],rt1[maxl*80],rt2[maxl*80];
struct node
{
	ll sum;
	int ls,rs;
}tr[maxl*80];

inline void upd(int &o,int l,int r,int x,int val)
{
	if(!o) o=++tot;
	if(l==r)
	{
		tr[o].sum+=val;
		return;
	}
	int mid=(l+r)>>1;
	if(x<=mid)
		upd(tr[o].ls,l,mid,x,val);
	else
		upd(tr[o].rs,mid+1,r,x,val);
	tr[o].sum=tr[tr[o].ls].sum+tr[tr[o].rs].sum;
}

inline void updbit(int i,int x,int val)
{
	while(i<=n)
		upd(rt[i],1,len,x,val),i+=i&-i;
}

inline void prework()
{
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;i++)
		scanf("%d",&a[i]);
	for(int i=1;i<=n;i++)
		updbit(i,a[i],a[i]);
}

inline void prert(int l,int r)
{
	cnt1=cnt2=0;
	for(int i=l-1;i;i-=i&-i)
		rt1[++cnt1]=rt[i];
	for(int i=r;i;i-=i&-i)
		rt2[++cnt2]=rt[i];
}

inline ll ask(int l,int r,int k)
{
	ll ans=0;
	if(r==k)
	{
		for(int i=1;i<=cnt1;i++)
			ans-=tr[rt1[i]].sum;
		for(int i=1;i<=cnt2;i++)
			ans+=tr[rt2[i]].sum;
		return ans;
	}
	int mid=(l+r)>>1;
	if(k<=mid)
	{
		for(int i=1;i<=cnt1;i++)
			rt1[i]=tr[rt1[i]].ls;
		for(int i=1;i<=cnt2;i++)
			rt2[i]=tr[rt2[i]].ls;
		return ask(l,mid,k);
	}
	else
	{
		for(int i=1;i<=cnt1;i++)
			ans-=tr[tr[rt1[i]].ls].sum;
		for(int i=1;i<=cnt2;i++)
			ans+=tr[tr[rt2[i]].ls].sum;
		for(int i=1;i<=cnt1;i++)
			rt1[i]=tr[rt1[i]].rs;
		for(int i=1;i<=cnt2;i++)
			rt2[i]=tr[rt2[i]].rs;
		return ans+ask(mid+1,r,k);
	}
}

inline void mainwork()
{
	for(int i=1;i<=m;i++)
	{
		int op,x,y;
		scanf("%d%d%d",&op,&x,&y);
		if(op==1)
		{
			updbit(x,a[x],-a[x]);
			a[x]=y;
			updbit(x,y,y);
		}
		else
		{
			ll now=1,s=0;
			while(1)
			{
				prert(x,y);
				ll t=min(now,1ll*len);
				ll tmp=ask(1,len,t);
				if(tmp==s)
				{
					printf("%lld\n",now);
					break;
				}
				s=tmp,now=s+1;
			}
		}
	}
}


int main()
{
	prework();
	mainwork();
	return 0;
}

 

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值