CF1641C
CF1641C
思路
考虑对每一个点维护这四个信息:
\(LP[x]\) :以 \(x\) 点为右端点的,最大的可以使得区间 \([LP[x],x]\) 当中 存在未知信息但一定有一个1 的左端点。
\(RP[x]:\) 以 \(x\) 点为左端点的,最小的可以使得区间 \([x,RP[x]]\) 当中 存在未知信息但一定有一个1 的右端点。
\(R[x]:\) \(x\) 的右边第一个未知信息的点
\(L[x]:\) \(x\) 的左边第一个未知信息的点
如果我们知道了这四个数组,那么如果现在有 \(LP[x]=x\) 或者 \(RP[x]=x\) ,即 \(x\) 这个位置为 \(1\)
(接下来,我们把 \([x,RP[x]]\) 称为以 \(x\) 为左端点的“最简区间”,把 \([LP[x],x]\) 称为以 \(x\) 为右端点的“最简区间”)
现在我们使用并查集解决这个问题,我们使用两个并查集,分别维护当前点前面第一个未知信息的点和后面第一个未知信息的点。
初始时,我们把每个点 \(LP\) 设为 \(0\) ,\(RP\) 设为 \(n+1\)
分别考虑两种操作该怎么更新信息:
-
对于将一个区间更新为:存在未知的信息但是存在 1 的操作:
这个很简单,只需要更新 \(l\) 点处的 \(RP[l]=min(RP[l],r)\) 就行了,如果 \(l\) 是 \(0\) ,那么就是找到 \(l\) 右边第一个 \(pos\) ,这个点不为 \(0\) ,更新 \(RP[pos]=min(RP[pos],r)\) 即可。
void U1(int l,int r){//更新区间 [l,r] 的“最简区间” l=R.find(l),r=L.find(r);//找到 l 右边第一个未知信息的点,r 左边第一个未知信息的点 RP[l]=min(RP[l],r);//更新l的右端 LP[r]=max(LP[r],l);//更新r的左端 }
-
对于把一个区间全部置为 \(0\) 的操作:
找到这个区间当中还存在的,未知信息的点,显然我们可以通过刚刚维护的并查集来实现,并把这些点设为 \(0\)
(相当于并查集就成为链表把这些点串起来了)
在把这些点设为 \(0\) 的过程中,考虑要更新的信息:
(比如现在把点 \(x\) 设为 \(0\))
\(L[x]\) 更新为 \(L[x-1]\) ,\(R[x]\) 更新为 \(R[x+1]\)
更新以 \(x\) 作为 左/右 端点的“最简区间”。
(其实这两步等价于插入区间 \([LP[x],x-1],[x+1,RP[x]]\),具体可以结合代码理解)
int LP[N],RP[N]; void U1(int l,int r){//更新区间 [l,r] 的“最简区间” l=R.find(l),r=L.find(r);//找到 l 右边第一个未知信息的点,r 左边第一个未知信息的点 RP[l]=min(RP[l],r);//更新l的右端 LP[r]=max(LP[r],l);//更新r的左端 } void U0(int x){//把x变成0 R.fa[x]=x+1;//更新 x 的右边第一个未知信息的点 L.fa[x]=x-1;//更新 x 的左边第一个未知信息的点 U1(x,RP[x]); U1(LP[x],x);//更新LP,RP数组 RP[x]=LP[x]=-1;//点 x 不可能成为答案了,并且之后也不会被访问到 } for(int v=R.find(l);v<=r;v=R.find(v+1)) U0(v);//遍历当前要删除的区间 [l,r]
综上,时间复杂度 \(O(n\alpha(n))\) 。
代码
#include<bits/stdc++.h>
using namespace std;
const int N=2e5+50;
int LP[N],RP[N];
int n,q;
struct UPS{
int fa[N];
int find(int x){return fa[x]==x?x:fa[x]=find(fa[x]);}
}L,R;
void U1(int l,int r){
l=R.find(l),r=L.find(r);
RP[l]=min(RP[l],r);
LP[r]=max(LP[r],l);
}
void U0(int x){//把x变成0
R.fa[x]=x+1;L.fa[x]=x-1;
U1(x,RP[x]);U1(LP[x],x);
RP[x]=LP[x]=-1;
}
int main(){
ios::sync_with_stdio(0);
cin>>n>>q;
for(int i=0;i<=n+1;i++) L.fa[i]=i,R.fa[i]=i,RP[i]=n+1;
for(int i=1;i<=q;i++){
int opt;cin>>opt;
if(opt==0){
int l,r,x;
cin>>l>>r>>x;
if(x==0) for(int v=R.find(l);v<=r;v=R.find(v+1)) U0(v);
else U1(l,r);
}
else{
int x;
cin>>x;
if(LP[x]==-1) puts("NO");
else if(RP[x]==x) puts("YES");
else puts("N/A");
}
}
}
其他
赛后看见 \(tyy\) 的代码发现只需要维护 \(LP,RP\) 当中的一个就好了,另外一个是多余的。
可以见 tyy的提交 ,被tyy打爆了。