CF1588F Jumping Through the Array:根号重构练习题

CF1588F Jumping Through the Array

维护一个由若干环组成的图,点有点权,支持:

  1. \(\sum_{i=l}^r a_i\)
  2. 给一个环上的 \(a_i\to a_i+v\)
  3. 断开 / 合并两个环。

\(n\le 2\times 10^5,8s\)

因为 \(p\) 是排列,所以形成的是环而不是基环树。

三操作非常特殊,我们可以先考虑没有三操作的情况。

问题在于,如何快速计算一个区间 \([l,r]\) 在多个环上有多少元素。我们不光要保证能快速计算,还要保证有用的环尽可能少。

八秒的时限实际上已经提示了往根号的角度去想,无非三种:序列分块,值域分块,时间分块。

序列分块并不能保证环的个数尽可能少,我们可能会想对值域分块(根号分治)。

将环分为大环和小环,每次询问暴力算大环的tag,小环暴力修改,没有操作三看似是好做的,实际上加上操作三也能完成,详见 fireinice的题解

对于树上/图上带修改的数据结构问题,如果没能想到 \(\log\) 数据结构,时间分块(定期重构)通常是非常优秀的暴力(甚至正解)。其无非解决两个问题:散块对查询的影响,整块的整体重构。

但本题能评为 3500 的原因还在于,定期重构提供了更多的性质。我们将整块内进行操作二和操作三的节点看作关键点,从一个关键点到下一个关键点之间的信息是不重要的,在修改和询问中他们都等价。我们可以将他们缩起来,这样总共的点数是 \(O(B)\) 的。

由此操作二三就变得简单了,前者我们直接打 tag 即可,后者可以直接交换两个关键点。

对于整块的整体重构,我们首先要求出所有接下来的关键点,并将他们缩点,再将 tag 下放的原始数组 \(a\) 上。

对于操作一的查询,解决区间 \([l,r]\) 在一个环上有多少元素,可以拆成前缀和,每个有用的环开前缀和数组 \(s_i\) 表示这个环上编号 \(\le i\) 的有多少点。注意到有用的只有查询的 \(O(B)\) 个点 \((l_1-1,r_1),(l_2-1,r_2)...(l_B-1,r_B)\),因此可以离散化,在整体重构的过程中处理即可。每次整体重构复杂度 \(O(n+B\log )\),询问复杂度 \(O(B+\log)\)。其中 \(\log\) 均为离散化的复杂度。

#include <bits/stdc++.h>
#define ll long long
#define pii pair<int,ll>
#define fi first
#define se second
using namespace std;
inline int read(){
	char c=getchar();int h=0,tag=1;
	while(!isdigit(c)) tag=(c=='-'?-1:1),c=getchar();
	while(isdigit(c)) h=(h<<1)+(h<<3)+(c^48),c=getchar();
	return h*tag;
}
void fil(){
	freopen("a.in","r",stdin);
	freopen("data.out","w",stdout);
}
int tot/*from 1*/;int tot2;
const int N=3e5+500*2,SN=800;
int key[N],vis[N],to[N],symbol[N],sum[SN*2][SN*2],p[N],qvis[N],tmp_sum[N],b[N];
ll tagkey[N],suma[N],a[N],tag[N];
vector<int>vec/*key node*/,query_vec,ask_pos;
int n;
int U,Q;
void dfs(int x) {
	if(vis[x]) {	
		query_vec.push_back(x);
		if(vec.size()) {
			++tot;
			query_vec.push_back(vec[0]);
			to[tot]=x;
			for(int y:vec) {
				symbol[y]=tot;
			}
			vec.clear();
		}
		return ;
	}
	vis[x]=1;	
	if(!key[x]) {
		vec.push_back(x);
	}else{
		query_vec.push_back(x);
		if(vec.size()!=0) {
			++tot;
			query_vec.push_back(vec[0]);
			to[tot]=x;
			for(int y:vec) {
				symbol[y]=tot;
			}
			vec.clear();
		}
	}
	dfs(p[x]);
}
void dfs2(int x) {
	if(vis[x]) return ;
	vis[x]=1;
	if(!key[x]) {
		a[x]+=tag[symbol[x]];
		dfs2(p[x]);
	}else{
		a[x]+=tagkey[x];
		dfs2(p[x]);
	}
}
ll ans;
void query_dfs(int rt,int x,int l,int r,int nl,int nr) {
	if(qvis[x]) return ;
	qvis[x]=1;
	if(key[x]) {
		if(x<=r&&l<=x) ans+=tagkey[x];
		query_dfs(rt,p[x],l,r,nl,nr);
	} else{
		ans+=1ll*(sum[symbol[x]][nr]-sum[symbol[x]][nl])*tag[symbol[x]];
		query_dfs(rt,to[symbol[x]],l,r,nl,nr);
	}
}
inline ll query(int l,int r) {
	ans=suma[r]-suma[l-1];
	int nl=lower_bound(b+1,b+1+tot2,l-1)-b,nr=lower_bound(b+1,b+1+tot2,r)-b;
	for(int x:query_vec) {
		if(!qvis[x]) query_dfs(x,x,l,r,nl,nr);
	}
	for(int x:query_vec) {
		qvis[x]=0;
	}
	return ans;
}
void upd_dfs(int rt,int cnt,int x,ll v) {
	if(x==rt) {
		if(cnt==0) cnt=1;
		else return ;
	}
	if(key[x]) {
		tagkey[x]+=v;
		upd_dfs(rt,cnt,p[x],v);
	}else{
		tag[symbol[x]]+=v;
		upd_dfs(rt,cnt,to[symbol[x]],v);
	}
}
void clear_upd(int x) {
	if(!vis[x]) return ;
	vis[x]=0;
	if(key[x]) {
		clear_upd(p[x]);
	}else{
		clear_upd(to[symbol[x]]);
	}
}
struct node{
	int typ,l,r;
}ask[N],q[N];
int main(){
//	fil();
	n=read();
	for(int i=1;i<=n;i++) a[i]=read();
	for(int i=1;i<=n;i++) p[i]=read();
	int m=read();
	int B=600;
	for(int i=1;i<=m;i++) {
		int typ=read(),l=read(),r=read();
		ask[i]=node{typ,l,r};
	}
	int cnt=0;
	for(int i=1;i<=min(m,(n>150000?(m*2):(10000500)));i++) {
		q[++cnt]=ask[i];
		if(cnt==1) {
			for(int j=1;j<=n;j++) {
				if(!vis[j]) dfs2(j);
			}
			for(int j=1;j<=n;j++) suma[j]=suma[j-1]+a[j];
			for(int j=1;j<=n;j++) tag[j]=vis[j]=key[j]=symbol[j]=to[j]=tagkey[j]=0;
			for(int j=1;j<=tot;j++) {
				for(int k=1;k<=tot2;k++) sum[j][k]=0;
			}
//			memset(tag,0,sizeof tag);memset(vis,0,sizeof vis);
//			memset(key,0,sizeof key);memset(symbol,0,sizeof symbol);
			tot=0;
			int cnt2=0;
			for(int j=i;j<=i+B-1&&j<=m;j++) {
				if(ask[j].typ==2) {	
					key[ask[j].l]=1;
				}
				if(ask[j].typ==3) {
					key[ask[j].l]=key[ask[j].r]=1;
				}
				if(ask[j].typ==1) {
					b[++cnt2]=ask[j].l-1;b[++cnt2]=ask[j].r;
				}
			}
			sort(b+1,b+1+cnt2); tot2=unique(b+1,b+1+cnt2)-b-1;
			query_vec.clear();
			for(int j=1;j<=n;j++) {
				if(key[j]&&!vis[j]) dfs(j);
			}
			for(int j=1,k=1;j<=n&&k<=tot2;) {
				while(j<=b[k]) {
					sum[symbol[j]][k]++;
					j++;
				}
				k++;
			}
			for(int j=1;j<=tot;j++) {
				for(int k=1;k<=tot2;k++) sum[j][k]+=sum[j][k-1];
			}
			for(int j=1;j<=n;j++) vis[j]=0;
		}
		if(cnt==B) {
			cnt=0;
		}
		if(ask[i].typ==1) {		
			int l=ask[i].l,r=ask[i].r;
			printf("%lld\n",query(l,r));
		}
		if(ask[i].typ==2) {	
			upd_dfs(ask[i].l,0,ask[i].l,ask[i].r);
		}
		if(ask[i].typ==3) {
			swap(p[ask[i].l],p[ask[i].r]);
		}
	}
	return 0;
}

md cf交了整整一页

posted @ 2024-08-27 08:50  Apricity8211  阅读(12)  评论(0编辑  收藏  举报