题解:P11831 [省选联考 2025] 追忆(民间数据)
Petit_Souris · · 题解
前言
第一次正赛场切黑题。省选续命成功了(虽然 Day 2 爆了但是肯定能进队对吧),希望最后四个月的 OI 生涯能让这第一次不是最后一次。
题解
下文分析认为
和有向图可达性相关了,结合 6s 2048MB 的时空限制,倾向于思考
那么我们每次查询时要做的事情大致可以分为三部分:
-
Part 1:求出
x 可达的点集S ; -
Part 2:求出
a_i\in [l,r] 的点集T ,并与S 求交(S\leftarrow S\cap T )。需要支持op=1 的修改。 -
Part 3:求出
S 中b 最大的点。需要支持op=2 的修改。
一步一步来做。
Part 1
这部分是经典的:对于每个节点
查询的时候直接调用
Part 2
这部分需要做区间查询。我们发现,bitset 的查询可以用 ST 表处理,因为重合的部分可以并起来,不会多算。因此我们可以考虑用 ST 表处理。
但是这样预处理的时空复杂度就已经来到了
这样做的时间复杂度是
至于修改,再套一层定期重构就行了。每
Part 3
这部分需要查询一个集合中的最大值。由于 _Find_first() 直接找到最大值。
这里我赛时想到的方法是:对
修改是非常容易的,直接枚举后缀就行了,复杂度只有
复杂度
至此整题已被解决,常数非常小,洛谷 5s 通过。
其实这里每个块长大概要取什么是容易平衡出来的,都应该是
#include<bits/stdc++.h>
typedef int ll;
#define pii pair<ll,ll>
#define rep(i,a,b) for(ll i=(a);i<=(b);++i)
#define per(i,a,b) for(ll i=(a);i>=(b);--i)
using namespace std;
bool Mbe;
ll read(){
ll x=0,f=1;char ch=getchar();
while(!isdigit(ch)){if(ch=='-')f=-f;ch=getchar();}
while(isdigit(ch))x=x*10+ch-'0',ch=getchar();
return x*f;
}
void write(ll x){
if(x<0)putchar('-'),x=-x;
if(x>9)write(x/10);
putchar(x%10+'0');
}
const ll N=1e5+9,M=2e5+9;
ll typ,T,n,m,q,U[M],V[M];
ll a[N],b[N],ord_a[N],ord_b[N];
ll stk[N*2],top;
bitset<N>go[N];
bitset<N>st[9][135];
bitset<N>btb[50],tmp;
ll lg[150],S,L[N],R[N],bel[N];
ll b_S,blb[N];
void First_Build(){
S=min(n,(n+127)/128);
rep(i,0,n+1)L[i]=R[i]=bel[i]=0;
rep(i,1,n){
bel[i]=(i-1)/S+1;
if(!L[bel[i]])L[bel[i]]=i;
R[bel[i]]=i;
}
b_S=max(1,n/16);
rep(i,1,n)blb[i]=(i-1)/b_S+1;
rep(i,0,blb[n]+1)btb[i].reset();
rep(i,1,n){
rep(j,1,blb[i])btb[j].set(ord_b[i]);
}
}
void Rebuild(){
top=0;
rep(i,1,n)ord_a[a[i]]=i;
rep(i,1,bel[n])st[0][i].reset();
rep(i,1,bel[n]){
rep(j,L[i],R[i])st[0][i].set(ord_a[j]);
}
rep(k,1,lg[bel[n]]){
rep(i,1,bel[n]-(1<<k)+1){
st[k][i]=st[k-1][i]|st[k-1][i+(1<<(k-1))];
}
}
}
void Query_st(ll l,ll r,bitset<N>&ans){
ll p=lg[r-l+1];
ans|=st[p][l]|st[p][r-(1<<p)+1];
}
void Query(ll l,ll r,bitset<N>&ans){
ll bl=bel[l],br=bel[r];
if(bl==br){
rep(i,l,r)ans.set(ord_a[i]);
return ;
}
rep(i,l,R[bl])ans.set(ord_a[i]);
rep(i,L[br],r)ans.set(ord_a[i]);
if(bl+1<=br-1)Query_st(bl+1,br-1,ans);
}
vector<ll>to[N];
void solve(){
n=read(),m=read(),q=read();
rep(i,1,m)U[i]=read(),V[i]=read();
rep(i,0,n+1)to[i].clear();
rep(i,1,m)to[U[i]].push_back(V[i]);
rep(i,1,n)a[i]=read();
rep(i,1,n)b[i]=read(),ord_b[b[i]]=i;
rep(i,0,n+1)go[i].reset();
rep(i,1,n)go[i].set(i);
per(i,n,1){
for(ll j:to[i])go[i]|=go[j];
}
ll rebuild_lim=max(1,q/64); // lim can't be 0
First_Build(),Rebuild();
top=0;
ll C1=0,C2=0,C3=0;
rep(i,1,q){
ll op=read();
// cout<<"going: "<<i<<" "<<op<<endl;
if(op==1){
C1++;
ll x=read(),y=read();
stk[++top]=x,stk[++top]=y;
swap(ord_a[a[x]],ord_a[a[y]]);
swap(a[x],a[y]);
}
else if(op==2){
C2++;
ll x=read(),y=read();
ll frm=blb[b[x]],to=blb[b[y]];
if(frm<to){
rep(i,frm+1,to)btb[i].set(x);
}
else if(frm>to){
rep(i,to+1,frm)btb[i].reset(x);
}
swap(frm,to);
if(frm<to){
rep(i,frm+1,to)btb[i].set(y);
}
else if(frm>to){
rep(i,to+1,frm)btb[i].reset(y);
}
swap(ord_b[b[x]],ord_b[b[y]]);
swap(b[x],b[y]);
}
else {
C3++;
ll x=read(),l=read(),r=read();
tmp.reset();
Query(l,r,tmp);
rep(j,1,top){
ll x=stk[j];
if(a[x]<l||a[x]>r)tmp.reset(x);
else tmp.set(x);
}
// rep(j,1,n)cout<<tmp[j];
// cout<<endl;
tmp&=go[x];
ll ql=1,qr=blb[n],pos=0;
while(ql<=qr){
ll mid=(ql+qr)>>1;
if((btb[mid]&tmp).any())pos=mid,ql=mid+1;
else qr=mid-1;
}
if(!pos){
write(0),putchar('\n');
goto bed;
}
per(j,min(n,pos*b_S),(pos-1)*b_S+1){
ll x=ord_b[j];
if(tmp[x]){
write(j),putchar('\n');
goto bed;
}
}
}
bed:
if(i%rebuild_lim==0)Rebuild();
}
// cerr<<C1<<" "<<C2<<" "<<C3<<"\n";
}
bool Med;
int main(){
cerr<<fabs(&Med-&Mbe)/1048576.0<<"MB\n";
freopen("recall.in","r",stdin);
freopen("recall.out","w",stdout);
typ=read(),T=read();
rep(i,2,128)lg[i]=lg[i>>1]+1;
while(T--)solve();
cerr<<clock()*1.0/CLOCKS_PER_SEC*1000<<"ms\n";
return 0;
}
/*
g++ -std=c++14 -O2 -Wall -Wextra recall.cpp -o recall && ./recall
*/