题解:[省选联考 2025] 追忆
题意分析
下文中的集合均使用 bitset 维护。
考虑 DAG 维护连通性太难了,对于每个点维护其能够到达的点是 \(\mathcal O\left(n^2\right)\) 的,不可接受。事实上,可以证明 DAG 连通性最优只能 bitset 做到 \(\mathcal O\left(\dfrac{n^2}w\right)\)。
具体而言,开 \(n\) 个 bitset \(f_1,f_2,\cdots,f_n\),\(f_i[j]\) 表示 \(i\) 能否到达 \(j\),这个可以用 bitset 在倒序拓扑序上递推,\(\mathcal O\left(\dfrac{nm}w\right)\) 得到。
之后考虑处理 \(q\) 次询问 \(\operatorname{query}(x,l,r)\),答案就是:
暴力做是 \(\mathcal O\left(qn\right)\) 的,不可接受。
需要优化的就是单次求解 \(\operatorname{query}(x,l,r)\)。首先对于 \(f_x\cap\set{i\mid a_i\in[l,r]}\),显然想到继续利用 bitset,让 \(f_x\) 与上另一个 bitset。可以想到把 \(\set{i\mid a_i\in[l,r]}\) 做成一个 bitset,但是 \(\mathcal O(n)\) 不能接受,所以可以考虑分块。
分块就对 \(a_i\) 值域分块,令块长为 \(B\),\(s_i=\set{i\mid a_i\in[(i-1)B+1,iB]}\)。再维护一下 \(\textit{idA}_{a_i}=i\) 就可以 \(\mathcal O\left(B+\dfrac nB\cdot\dfrac nw+\dfrac nw\right)\) 求出 \(f_x\cap\set{i\mid a_i\in[l,r]}\)。
更进一步地,可以维护 \(s_i\) 的前缀和,这样求解 \(f_x\cap\set{i\mid a_i\in[l,r]}\) 的复杂度可以降到 \(\mathcal O\left(B+\dfrac nw\right)\)。修改 \(a\) 显然是 \(\mathcal O\left(\dfrac nB\right)\) 的。
记 \(\textit{pl}=f_x\cap\set{i\mid a_i\in[l,r]}\),接下来就是求解 \(\max(pl)\)。直接做是 \(\mathcal O(n)\) 的,即使用 bitset 的 _Find_first 和 _Find_next 也过不了。
从 \(\textit{pl}\) 里枚举 \(i\) 再求解最大的 \(b_i\) 的方法没有前途了,考虑从大到小枚举 \(b_i\),判断 \(i\) 是否在 \(\textit{pl}\) 里。维护 \(\textit{idB}_{b_i}=i\),\(n\rightarrow 1\) 暴力枚举是 \(\mathcal O(n)\) 的,考虑优化。
同样分块,维护 \(t_i=\set{i\mid b_i\in[(i-1)B+1,iB]}\)。\(\operatorname{pos}(n)\rightarrow 1\) 枚举块 \(t_i\),判断 \(\textit{pl},t_i\) 是否有交,有交就在块内从大到小 \(\mathcal O(B)\) 枚举即可。
于是这一部分的复杂度从 \(\mathcal O(n)\) 变成了 \(\mathcal O\left(\dfrac nB\cdot\dfrac nw+B\right)\)。显然和 \(\mathcal O(n)\) 谁跑得快不好说,继续优化。
容易发现我们是从大到小枚举,我们只希望找到最大的 \(i\) 使得 \(t_i,\textit{pl}\) 有交。可以维护 \(t_i\) 的后缀并集(后缀和),这样就有了单调性,可以二分。
于是优化成了 \(\mathcal O\left(\dfrac nw\log\dfrac nB+B\right)\)。修改 \(b\) 的复杂度对应也是 \(\mathcal O\left(\dfrac nB\right)\)。
总时间复杂度即为 \(\mathcal O\left(\dfrac{nm}w+qB+q\dfrac nw\log\dfrac nB\right)\)。
发现带个 \(\log\),不好用基本不等式之类的东西求最小值,尝试 \(B=\sqrt n\),时间复杂度为 \(\mathcal O\left(\dfrac{nm}w+q\sqrt n+q\dfrac nw\log\sqrt n\right)\)。算了一下发现能过,测试一下也能过。
显然 \(\dfrac{nm}w,q\) 都没用,换而言之,我们只需要求解下式最小值:
\(\log_2\dfrac nB\) 不好做,将其变式:
打表发现是个凸函数,实际上也是的。记 \(g(B)=B-\dfrac nw\log_2 B,f(B)=g(B)+\dfrac nw\log_2 n\)。
\(g(B)\) 显然是两个凸函数相加,也是凸函数,于是 \(f(B)\) 也是凸函数。接下来求解最小值,求导:
因为是凸函数,所以令 \(f'(B)=0\),得 \(B=\dfrac n{w\ln 2}\) 时复杂度最优。
此时总复杂度为 \(\mathcal O\left(\dfrac{nm}w+q\dfrac n{w\ln 2}+q\dfrac nw\log(w\ln 2)\right)\)。
AC 代码
取块长 \(B=\max\left(\left\lfloor\dfrac n{w\ln2}\right\rfloor,1\right)\),时间复杂度 \(\mathcal O\left(\dfrac{nm}w+q\dfrac n{w\ln 2}+q\dfrac nw\log(w\ln 2)\right)\)。因为常数的原因,和 \(B=\sqrt n\) 谁跑得快不好说。总共 3s 的差距更可能是评测机波动。
//#include<bits/stdc++.h>
#include<algorithm>
#include<iostream>
#include<cstring>
#include<iomanip>
#include<cstdio>
#include<string>
#include<vector>
#include<cmath>
#include<ctime>
#include<deque>
#include<queue>
#include<stack>
#include<list>
#include<bitset>
using namespace std;
constexpr const int N=1e5,M=2e5,Q=1e5,P=64*log(2),V=(N+N/P-1)/(N/P);
int n,q,B,a[N+1],idA[N+1],b[N+1],idB[N+1];
bitset<N+1>f[N+1],s[V+1],t[V+1];
vector<int>g[N+1];
int pos(int x){
return (x+B-1)/B;
}
int edgeL(int x){
return (x-1)*B+1;
}
int edgeR(int x){
return min(x*B,n);
}
void build(){
static int in[N+1];
fill(in+1,in+n+1,0);
for(int i=1;i<=n;i++){
for(int j:g[i]){
in[j]++;
}
}
static int q[N+1],front,rear;
front=rear=0;
for(int i=1;i<=n;i++){
if(!in[i]){
q[rear++]=i;
}
}
while(front<rear){
int x=q[front++];
for(int v:g[x]){
in[v]--;
if(!in[v]){
q[rear++]=v;
}
}
}
for(int i=rear-1;0<=i;i--){
int x=q[i];
f[x][x]=true;
for(int v:g[x]){
f[x]|=f[v];
}
}
for(int i=1;i<=n;i++){
s[pos(a[i])][i]=1;
t[pos(b[i])][i]=1;
}
for(int i=1;i<=pos(n);i++){
s[i]|=s[i-1];
}
for(int i=pos(n)-1;1<=i;i--){
t[i]|=t[i+1];
}
}
void clear(int n,int m,int q){
for(int i=1;i<=n;i++){
g[i].resize(0);
f[i]=0;
}
B=max(n/P,1);
for(int i=1;i<=pos(n);i++){
s[i]=0;
t[i]=0;
}
}
void swapA(int x,int y){
for(int i=pos(a[x]);i<=pos(n);i++){
s[i][x]=0;
}
for(int i=pos(a[y]);i<=pos(n);i++){
s[i][y]=0;
}
swap(a[x],a[y]);
idA[a[x]]=x;
idA[a[y]]=y;
for(int i=pos(a[x]);i<=pos(n);i++){
s[i][x]=1;
}
for(int i=pos(a[y]);i<=pos(n);i++){
s[i][y]=1;
}
}
void swapB(int x,int y){
for(int i=pos(b[x]);1<=i;i--){
t[i][x]=0;
}
for(int i=pos(b[y]);1<=i;i--){
t[i][y]=0;
}
swap(b[x],b[y]);
idB[b[x]]=x;
idB[b[y]]=y;
for(int i=pos(b[x]);1<=i;i--){
t[i][x]=1;
}
for(int i=pos(b[y]);1<=i;i--){
t[i][y]=1;
}
}
int query(int x,int l,int r){
bitset<N+1>pl;
if(r-l+1<=2*B){
for(int i=l;i<=r;i++){
pl[idA[i]]=1;
}
}else{
pl=s[pos(r)-1]^s[pos(l)];
for(int i=l;i<=edgeR(pos(l));i++){
pl[idA[i]]=1;
}
for(int i=edgeL(pos(r));i<=r;i++){
pl[idA[i]]=1;
}
}
pl&=f[x];
int ans=0;
l=1,r=pos(n);
while(l<=r){
int mid=l+r>>1;
if((pl&t[mid]).any()){
ans=mid;
l=mid+1;
}else{
r=mid-1;
}
}
if(!ans){
return 0;
}
for(int i=edgeR(ans);edgeL(ans)<=i;i--){
if(pl[idB[i]]){
return i;
}
}
return 0;
}
int main(){
/*freopen("test.in","r",stdin);
freopen("test.out","w",stdout);*/
ios::sync_with_stdio(false);
cin.tie(0);cout.tie(0);
int c,T;
cin>>c>>T;
while(T--){
int m;
cin>>n>>m>>q;
clear(n,m,q);
while(m--){
int u,v;
cin>>u>>v;
g[u].push_back(v);
}
for(int i=1;i<=n;i++){
cin>>a[i];
idA[a[i]]=i;
}
for(int i=1;i<=n;i++){
cin>>b[i];
idB[b[i]]=i;
}
build();
for(int i=1;i<=q;i++){
int op,x,y,l,r;
cin>>op>>x;
switch(op){
case 1:
cin>>y;
swapA(x,y);
break;
case 2:
cin>>y;
swapB(x,y);
break;
case 3:
cin>>l>>r;
cout<<query(x,l,r)<<'\n';
break;
}
}
}
cout.flush();
/*fclose(stdin);
fclose(stdout);*/
return 0;
}

浙公网安备 33010602011771号