P11831 [省选联考 2025] 追忆 题解
题意
有一个 \(n\) 个点 \(m\) 条边的DAG。每个点有权值 \(a_i\) 和 \(b_i\)。有 \(q\) 次操作,每次操作为以下其中之一:
- 给出 $1\ x\ y $,交换 \(a_x\) 和 \(a_y\)。
- 给出 $2\ x\ y $,交换 \(b_x\) 和 \(b_y\)。
- 给出 \(3\ x\ l\ r\),求出在 \(x\) 可达的点集 \(C\) 中,找到 \(y\in C\),满足 \(l\le a_y\le r\),求这样的 \(y\) 中最大的 \(b_y\)。
保证给出的边 \((u,v)\) 满足 \(u<v\)。
\(n,q\le 10^5,m\le 2\times 10^5\)。
思路
首先本题的查询需要满足三个: \(x\) 可达、 \(a\in [l,r]\),以及最大的 \(b\)。其中可达性是不会改变的,其他的 \(a,b\) 值可能被修改。
首先对于DAG的可达性,不难想到用bitset去在 \(O(\frac{nm}w)\) 的时间复杂度求出。因为对于DAG,确实很难用一些其他带 $\log $ 的方式解决。
具体就是对于每一个节点 \(i\) 开一个bitset\(G\),如果 \(G_x\) 的第 \(y\) 位是1,则说明 \(x\) 可达 \(y\)。可以在DAG上用按位或求出。
接下来先考虑对于 \(a\) 的处理。
因为可达性我们已经用bitset 处理了,需要让后续的处理能与此处的bitset 快速结合。因此,此处我们同样考虑用bitset处理 \(a\)。
但是 \(a\) 需要满足一个值域上的 \([l,r]\) 之间,所以考虑同样开 \(n\) 个bitset \(A\)。对于 \(A_i\) ,若其第 \(y\) 位为1,则说明当前 \(a_y\ge i\)。
于是在 \((A_{r+1}\ \text{xor}\ A_{l})\) 中是 1 的位置对应的点的 \(a\) 值一定在 \([l,r]\) 之间。
但是,我们开不下 \(2n\) 个长为 \(n\) 的 bitset,而且进行修改 \(a\) 时需要改 \(O(n)\) 个bitset,会挂掉。而可达性的bitset 又难以优化。所以考虑优化 \(A\)。
我们可以考虑用分块去优化,块长为 \(T\)。对于整块去维护上述一样的后缀,散块暴力求值即可。这样就只用开 \(\frac nT\) 个bitset了。
进行修改时只用修改 $\frac{n}T $ 个块。而求在 \([l,r]\) 的 \(A\) 也只需 \(O(\frac nw+T)\)。
我们求出了在 \([l,r]\) 的 \(A\) 后,与 \(G_x\) 进行按位与,即可求到合法的 \(y\) 的集合 \(C\)。
此时已经求出了合法的 \(y\) 的集合。考虑如何在这个用bitset 表示的集合中找出最大的 \(b_y\)。
这里还可以再用bitset去与 \(a\) 一样维护 \(b\) 值,设其为 \(B\)。这样修改也可以 \(O(\frac nT)\)。
我们需要找到 \(C\) 中 \(b\) 值最大的值所在的块,也就是与 \(C\) 进行按位与后存在 1 位的最大的 \(B\) 的块。然后再暴力在块中找到最大的且在 \(C\) 中的值,输出即可。
但是这里找最大的 \(B\) 块不能直接枚举每个块再进行按位与,因为这样单次是 \(O(\frac {n^2} {Tw})\),完全过不了。
对于两个bitset进行按位与是 \(O(\frac nw)\),但只对一位求按位与就只需 \(O(1)\)。
我们不妨开一个指针 \(j\),初始化为 \(B\) 的第一个块。在对于 \(C\) 的每一位进行判断,如果 \(B_j\) 与 \(C\) 按位与后这一位是 1,就不断让 \(j\) 加一,直到这一位结果不为 1。
这样最后的 \(B\) 块就是 \(B_{j-1}\)。这样做时间复杂度是 \(O(\frac nw+\frac nT)\)。然后再在块中暴力找在 \(C\) 中的最大值,输出即可。
这道题就做完了,码量并不大。bitset 还是手写吧,能快点。
\(T\) 取 \(\sqrt n\) 最优,时间复杂度 \(O(\frac {nm}w+q(\frac nw+\sqrt n))\)。
#include<bits/stdc++.h>
using namespace std;
const int N=1e5+5;
const int W=1570,len=320;
int n,m,q,A[N],B[N],ida[N],k,idb[N];
//a[ida[x]]=x,b[idb[x]]=x;
vector<int> e[N];
#define ull unsigned long long
struct bitst//手写bitset
{
ull s[W];
void reset() {memset(s,0,sizeof(s));}
void set(int x) {s[(x>>6)]|=1ull<<(x&63);}
void flip(int x) {s[(x>>6)]^=1ull<<(x&63);}
void operator &=(const bitst &b)
{
for(int i=0;i<W;i++) s[i]&=b.s[i];
}
void operator |=(const bitst &b)
{
for(int i=0;i<W;i++) s[i]|=b.s[i];
}
void operator ^=(const bitst &b)
{
for(int i=0;i<W;i++) s[i]^=b.s[i];
}
int val(int x){ return s[(x>>6)]>>(x&63)&1;}
};
bitst g[N],a[len],b[len],c;
int read()
{
int x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
return x*f;
}
int L(int x){return x*len;}
int R(int x){return min(n,x*len+len-1);}//找块的左右端点
void solve()
{
n=read(),m=read(),q=read();
k=n/len;
for(int i=1;i<=n;i++) g[i].reset(),g[i].set(i),e[i].clear();
for(int i=1;i<=m;i++)
{
int u=read(),v=read();
e[u].push_back(v);
}
for(int i=n;i>=1;i--)
{
for(int j=0;j<e[i].size();j++) g[i]|=g[e[i][j]];//维护可达性
}
for(int i=0;i<=k+1;i++) a[i].reset(),b[i].reset();
for(int i=1;i<=n;i++) A[i]=read(),a[A[i]/len].set(i),ida[A[i]]=i;
for(int i=1;i<=n;i++) B[i]=read(),b[B[i]/len].set(i),idb[B[i]]=i;
for(int i=k-1;i>=0;i--) a[i]|=a[i+1],b[i]|=b[i+1];//预处理A、B bitset
while(q--)
{
int op=read(),x=read(),l=read(),r;
if(op==1)
{
for(int i=A[x]/len;i>=0;i--) a[i].flip(x);
for(int i=A[l]/len;i>=0;i--) a[i].flip(l);
swap(ida[A[x]],ida[A[l]]);
swap(A[x],A[l]);
for(int i=A[x]/len;i>=0;i--) a[i].flip(x);
for(int i=A[l]/len;i>=0;i--) a[i].flip(l);//修改A
}
else if(op==2)
{
for(int i=B[x]/len;i>=0;i--) b[i].flip(x);
for(int i=B[l]/len;i>=0;i--) b[i].flip(l);
swap(idb[B[x]],idb[B[l]]);
swap(B[x],B[l]);
for(int i=B[x]/len;i>=0;i--) b[i].flip(x);
for(int i=B[l]/len;i>=0;i--) b[i].flip(l);//修改B
}
else
{
r=read();
c=a[l/len];
if(r/len<k) c^=a[r/len+1];
for(int i=L(l/len);i<l;i++) c.flip(ida[i]);
for(int i=R(r/len);i>r;i--) c.flip(ida[i]);
c&=g[x];//求出可选的点集C
int pos=-1;
for(int i=0;i<W;i++)
{
while(c.s[i]&b[pos+1].s[i]) pos++;
}//找到C中b的最大值所在的块
if(pos==-1)
{
printf("0\n");
continue;
}
for(int j=R(pos);j>=L(pos);j--) //暴力在块中找
{
if(c.val(idb[j]))
{
printf("%d\n",j);
break;
}
}
}
}
}
int main()
{
int ID=read(),T=read();
while(T--) solve();
return 0;
}

浙公网安备 33010602011771号