[硫化铂]货币

货币

题目描述

在这里插入图片描述
在这里插入图片描述

题解

首先,如果没有强制在线这个条件的话,应该是相当好做的。
我们可以考虑将整个过程到这来,每次相当于将原来的一个连通块分裂成了两个。
如果我们记录下每个左端点所对应的右端点的话,可以发现,他会对一些区间的右端点进行延伸,相当于给这些区间赋一个最大值改变的标记。
由于我们的右端点是单调不减的,所以我们可以发现,每次我们赋值会使之改变的区间必然是一个连续的区间,我们可以通过线段树上二分找出这个区间,然后在更改就很容易维护我们答案的最大值。
可以发现,当我们合并一个大小为 x x x的集合与一个大小为 y y y的集合时,其在线段树上会影响的区间是 O ( min ⁡ ( x , y ) ) O\left(\min(x,y)\right) O(min(x,y))级别的。
所以我们可以用启发式合并来维护我们的集合,这样的话,我们就可以在 O ( n log ⁡ 2 n ) O\left(n\log^2n\right) O(nlog2n)的时间复杂度内解决这个不强制在线的问题了。

其实强制在线也可以用类似的方法解决。
我们观察到点 i i i的右端点为 max ⁡ j = 0 i − 1 n x t j \max_{j=0}^{i-1}nxt_j maxj=0i1nxtj,这其实也是对应的所有的颜色在 i i i右边的最近点中最远的一个。
我们发现,我们每次启发式合并两个集合,同样也只会影响 O ( min ⁡ ( x , y ) ) O\left(\min(x,y)\right) O(min(x,y))级别的后继。
而正着来的话,我们的右端点是不断变小的,也就是说,当我们更改 x x x时,原来一整个右端点为 n x t x nxt_x nxtx的区间会分裂成多个不超过 n x t x nxt_x nxtx的区间。
事实上,总共会出现的值相同连续区间的个数,是与我们反着来一样的,我们反着来是将多个值不同的区间合并成一个区间,我们正着来则是将一个区间分裂成多个值不同的区间。
由于整个过程中更改区间的总数相同,所以正着来操作次数自然也是 O ( n log ⁡ n ) O\left(n\log n\right) O(nlogn)的,对于每个区间的位置,我们同样可以在线段树上二分求出。

总时间复杂度 O ( n log ⁡ 2 n ) O\left(n\log^2n\right) O(nlog2n)

源码

#include<bits/stdc++.h>
using namespace std;
#define MAXN 100005
#define MAXM 200005
#define lowbit(x) (x&-x)
#define reg register
#define pb push_back
#define mkpr make_pair
#define fir first
#define sec second
#define lson (rt<<1)
#define rson (rt<<1|1)
typedef long long LL;
typedef unsigned long long uLL; 
typedef long double ld;
typedef pair<int,int> pii;
const int INF=0x3f3f3f3f;
const int mo=998244353;
const int mod=1e5+3;
const int inv2=499122177;
const int jzm=2333;
const int zero=2000;
const int n1=2000;
const int orG=3,ivG=332748118;
const long double Pi=acos(-1.0);
const double eps=1e-12;
template<typename _T>
_T Fabs(_T x){return x<0?-x:x;}
template<typename _T>
void read(_T &x){
	_T f=1;x=0;char s=getchar();
	while(s>'9'||s<'0'){if(s=='-')f=-1;s=getchar();}
	while('0'<=s&&s<='9'){x=(x<<3)+(x<<1)+(s^48);s=getchar();}
	x*=f;
}
template<typename _T>
void print(_T x){if(x<0){x=(~x)+1;putchar('-');}if(x>9)print(x/10);putchar(x%10+'0');}
int gcd(int a,int b){return !b?a:gcd(b,a%b);}
int add(int x,int y,int p){return x+y<p?x+y:x+y-p;}
void Add(int &x,int y,int p){x=add(x,y,p);}
int qkpow(int a,int s,int p){int t=1;while(s){if(s&1)t=1ll*t*a%p;a=1ll*a*a%p;s>>=1;}return t;}
int n,q,Typ,fa[MAXN],lastans,cnt[MAXN],siz[MAXN],ans[MAXM],nxt[MAXN],sta[MAXN],stak; 
set<int>s[MAXN],stp;
set<int>::iterator it,jt;
void makeSet(int x){for(int i=1;i<=x;i++)fa[i]=i;}
int findSet(int x){return fa[x]==x?x:fa[x]=findSet(fa[x]);}
void unionSet(int a,int b){int u=findSet(a),v=findSet(b);if(fa[u]^v)fa[u]=v;}
class SegmentTree{
	private:
		int maxx[MAXN<<2],mn[MAXN<<2],mx[MAXN<<2],lzy[MAXN<<2];
		void pushup(int rt){
			maxx[rt]=max(maxx[lson],maxx[rson]);
			mn[rt]=min(mn[lson],mn[rson]);
			mx[rt]=max(mx[lson],mx[rson]);
		}
		void pushdown(int rt,int l,int r){
			if(lzy[rt]){
				int mid=l+r>>1;
				mx[lson]=lzy[rt];lzy[lson]=lzy[rt];
				mx[rson]=lzy[rt];lzy[rson]=lzy[rt];
				mn[lson]=lzy[rt]-mid+1;mn[rson]=lzy[rt]-r+1;
				lzy[rt]=0;
			}
		}
	public:
		void build(int rt,int l,int r){
			if(l==r){maxx[rt]=mx[rt]=mn[rt]=(l==1)?n:INF;return ;} 
			int mid=l+r>>1;build(lson,l,mid);build(rson,mid+1,r);pushup(rt);
		}
		void insert(int rt,int l,int r,int ai,int aw){
			if(l>r||l>ai||r<ai)return ;int mid=l+r>>1;
			if(l==r){maxx[rt]=aw;return ;}pushdown(rt,l,r);
			if(ai<=mid)insert(lson,l,mid,ai,aw);
			if(ai>mid)insert(rson,mid+1,r,ai,aw);
			pushup(rt);
		}
		void modify(int rt,int l,int r,int al,int ar,int aw){
			if(l>r||l>ar||r<al||al>ar)return ;
			if(al<=l&&r<=ar){lzy[rt]=mx[rt]=aw;mn[rt]=aw-r+1;return ;}
			int mid=l+r>>1;pushdown(rt,l,r);
			if(al<=mid)modify(lson,l,mid,al,ar,aw);
			if(ar>mid)modify(rson,mid+1,r,al,ar,aw);
			pushup(rt);
		}
		int queryPoint(int rt,int l,int r,int al,int ar){
			if(l>r||l>ar||r<al||al>ar)return 0;
			if(al<=l&&r<=ar)return maxx[rt];
			int mid=l+r>>1,res=0;pushdown(rt,l,r);
			if(al<=mid)res=max(queryPoint(lson,l,mid,al,ar),res);
			if(ar>mid)res=max(queryPoint(rson,mid+1,r,al,ar),res);
			return res;
		}
		int queryLimit(int rt,int l,int r,int ak){
			if(l==r)return l;if(maxx[rt]<=ak)return r+1;
			int mid=l+r>>1;pushdown(rt,l,r);
			if(maxx[lson]<=ak)return queryLimit(rson,mid+1,r,ak);
			return queryLimit(lson,l,mid,ak);
		}
		int ask(){return mn[1];}
}T;
signed main(){
	//freopen("currency.in","r",stdin);
	//freopen("currency.out","w",stdout);
	read(n);read(q);read(Typ);makeSet(n);
	for(int i=1;i<=n;i++)s[i].insert(i),siz[i]=1,nxt[i]=INF,stp.insert(i);
	T.build(1,1,n);nxt[0]=n;
	for(int i=1;i<=q;i++){
		int u,v;read(u);read(v);
		u=(u+Typ*lastans-1)%n+1;
		v=(v+Typ*lastans-1)%n+1;
		if(findSet(u)==findSet(v)){
			if(!lastans)lastans=n;
			printf("%d\n",lastans);continue;
		}
		u=findSet(u),v=findSet(v);
		if(siz[u]<siz[v])swap(u,v);int las=0;
		for(auto x:s[v]){
			it=s[u].lower_bound(x);
			if(it!=s[u].end())nxt[x]=*it,sta[++stak]=x;else nxt[x]=INF;
			if(it!=s[u].begin()){it--;int y=*it;if(nxt[y]>x)nxt[y]=x,sta[++stak]=y;}
			if(las)nxt[las]=min(nxt[las],x);las=x;
		}
		stp.erase(*s[v].begin());
		stp.erase(*s[u].begin());
		for(auto x:s[v])s[u].insert(x);
		stp.insert(*s[u].begin());fa[v]=u;siz[u]+=siz[v];
		if((*stp.rbegin())!=nxt[0])nxt[0]=*stp.rbegin(),sta[++stak]=0;
		while(stak){
			int x=sta[stak--];if(x==n)continue;
			int ltp=T.queryPoint(1,1,n,x+1,x+1),las=x;
			T.insert(1,1,n,x+1,nxt[x]);
			while(las<n){
				int tmp=T.queryPoint(1,1,n,1,las+1);
				if(tmp>ltp||tmp>INF-1)break;
				int rp=T.queryLimit(1,1,n,tmp);
				T.modify(1,1,n,las+1,min(rp-1,n),tmp);las=rp-1;
			}
		}
		printf("%d\n",lastans=T.ask());
	}
	return 0;
}

谢谢!!!

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值