CF1588F Jumping Through the Array:根号重构练习题
CF1588F Jumping Through the Array
维护一个由若干环组成的图,点有点权,支持:
- 求 \(\sum_{i=l}^r a_i\)
- 给一个环上的 \(a_i\to a_i+v\)
- 断开 / 合并两个环。
\(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交了整整一页