P14532 [RMI 2018] 颜色 / Colors 题解
题意简述:
一个 \(n\) 点 \(m\) 条边的无向连通图,每个点有权值 \(a_i\),如果存在边 \((u,v)\) 可以进行操作 \(a_u\leftarrow \min(a_u,a_v)\),问是否可以将 \(a_i\) 变为 \(b_i\)。
\(n\le 1.5\times 10^5,m\le 2\times 10^5\)。
前置知识:
线段树分治、可撤销并查集。
暴力(47 pts)
考虑将 \(a_u\) 变为 \(b_u\) 的充要条件是:
其中记 \(l(u,v)\) 为一条 \(u\) 到 \(v\) 的路径所经过点的集合,此时我们可以通过令 \(i\in l(u,v)\) 有 \(a_i=b_u\) 来传递 \(b_u\),但不会使 \(a_i\) 过低而导致 \(a_i\) 无法通过操作 \(\min(a_u,a_v)\) 变成 \(b_i\)。
一个朴素的想法是从每个节点 \(i\) 出发进行 bfs,按照上面的条件找到所有可以到达的节点 \(j\),这样的时间复杂度是 \(O(\sum n^2)\) 的,理论可以获得 47 分。但是 Subtask 7 的测试点 1 数据有误。 \(nm\) 达到了\(2\times 10^{10}\)。
线段树分治 + 可撤销并查集优化 (100pts)
接下来考虑优化,由于我们比较关注连通性,于是考虑一条边 \((u,v)\) 什么时候是可以用于连接 \(u\) 和 \(v\) 的,假设当前需要传递 \(x\),发现需要满足:
即:
这样我们不难想到线段树分治,\((u,v)\) 这条边就作用于区间 \([\max(b_u,b_v),\min(a_u,a_v)]\) 上,每次进入一个新的节点,将该节点存储的边连上,离开时就断开连边,这里的连通性可以使用可撤销并查集维护。
至于判断可达性,当目前的线段树节点代表单点 \(x\) 时,就是在判断 \(x\) 的传递,我们可以对于所有 \(a_i=x\) 的 \(i\),将其祖先时间戳标为 \(x\),然后对于所有 \(b_i=x\) 的 \(i\),如果 \(i\) 的祖先时间戳为 \(x\) 就证明 \(a_i\) 可以变为 \(b_i\),这部分 \(a_i=x\) 与 \(b_i=x\) 的信息可以使用 vector 记录。
这样总体时间复杂度为 \(O(\sum n\log^2 n)\),可以获得 100 分。
Code
#include<bits/stdc++.h>
using namespace std;
int tt,n,m,a[150005],b[150005],vis[150005];
vector<int> ra[150005],rb[150005];
vector<int> tr[600005];
struct node{
int u,v;
}s[200005];
bitset<150005> fl;
class dsu{
private:
int f[150005],sz[150005];
stack<pair<int,int>> q;
public:
void init(){for(int i=1;i<=n;i++)f[i]=i,sz[i]=1;}
int getf(int x){while(f[x]!=x)x=f[x];return x;}
void merge(int u,int v){
if(u==v)return;
if(sz[u]<sz[v])swap(u,v);
f[v]=u;
sz[u]+=sz[v];
q.push({u,v});
}
void del(){
int u=q.top().first,v=q.top().second;
q.pop();
f[v]=v;
sz[u]-=sz[v];
}
int get_sz(){return q.size();}
}T;
void add(int p,int s,int t,int l,int r,int id){
if(l<=s&&t<=r){
tr[p].push_back(id);
return;
}
int mid=(s+t)>>1;
if(l<=mid)add(p<<1,s,mid,l,r,id);
if(r>mid)add(p<<1|1,mid+1,t,l,r,id);
}
void solve(int p,int l,int r){
int st=T.get_sz();
for(auto i:tr[p]){int fu=T.getf(s[i].u),fv=T.getf(s[i].v);T.merge(fu,fv);}
if(l==r){
for(auto i:ra[l])vis[T.getf(i)]=l;
for(auto i:rb[l])if(vis[T.getf(i)]==l)fl[i]=1;
}else{
int mid=(l+r)>>1;
solve(p<<1,l,mid);
solve(p<<1|1,mid+1,r);
}
while(T.get_sz()>st)T.del();
tr[p].clear();
}
int main(){
ios::sync_with_stdio(0);
cin.tie(0),cout.tie(0);
cin>>tt;
while(tt--){
cin>>n>>m;
for(int i=1;i<=n;i++)ra[i].clear(),rb[i].clear();
for(int i=1;i<=n;i++)cin>>a[i],ra[a[i]].push_back(i);
for(int i=1;i<=n;i++)cin>>b[i],rb[b[i]].push_back(i);
for(int i=1;i<=m;i++){
int x,y;
cin>>x>>y;
s[i]={x,y};
if(max(b[x],b[y])<=min(a[x],a[y]))add(1,1,n,max(b[x],b[y]),min(a[x],a[y]),i);
}
T.init();
fl.reset();
memset(vis,0,sizeof vis);
solve(1,1,n);
int flag=true;
for(int i=1;i<=n&&flag;i++)flag&=fl[i];
if(flag)cout<<"1\n";
else cout<<"0\n";
}
return 0;
}

浙公网安备 33010602011771号