洛谷 P5332 - [JSOI2019]精准预测(2-SAT+bitset+分块处理)

2022-10-28,,,

洛谷题面传送门

七月份(7.31)做的题了,题解到现在才补,不愧是 tzc

首先不难发现题目中涉及的变量都是布尔型变量,因此可以考虑 2-SAT,具体来说,我们将每个人在每个时刻的可能的状态表示出来。我们开两个二维变量 \(\text{Live}(i,t)\) 表示第 \(i\) 个人第 \(t\) 个时刻还活着,\(\text{Dead}(i,t)\) 则表示第 \(i\) 个人在第 \(t\) 个时刻已经死了,那么题目给出的条件即对应这些布尔变量的一些推导关系,具体来说:

由于一个人死了不能复活,所以必有 \(\text{Dead}(i,t)\to\text{Dead}(i,t+1)\),同样地有逆否命题 \(\text{Live}(i,t+1)\to\text{Live}(i,t)\)
对于条件 \(0\)​,显然可以连边 \(\text{Dead}(x,t)\to\text{Dead}(y,t+1)\),同理逆否命题 \(\text{Alive}(y,t+1)\to\text{Alive}(x,t)\)
对于条件 \(1\),显然可以连边 \(\text{Alive}(x,t)\to\text{Dead}(y,t)\),同理逆否命题 \(\text{Alive}(y,t)\to\text{Dead}(x,t)\)

那么我们只要对建出来的图跑一遍 Tarjan,那么对于一个 \(i\)​​​,可能与它活到 \(T+1\)​​​ 时刻的居民数就是 \(n-1\)​​​ 再减去满足 \(\text{Alive}(i,T+1)\)​​​ 可以推到 \(\text{Dead}(j,T+1)\)​​​ 的居民 \(j\)​​​ 的数量。注意,如果一个居民 \(j\)​​​ 满足 \(\text{Alive}(j,T+1)\)​​​ 可以推到 \(\text{Dead}(j,T+1)\)​​​,那么它无论如何在 \(T+1\)​​​ 时刻都是死亡状态,对于这样的居民我们特判一下即可,具体来说,我们对于每个 \(i\)​​​ 去掉 \(\text{Alive}(i,T+1)\)​​​ 可以推到 \(\text{Dead}(j,T+1)\)​​​ 的点 \(j\)​​​ 的数量后,还需去掉满足 \(\text{Alive}(i,T+1)\)​​​ 推不到 \(\text{Dead}(j,T+1)\)​​​,但 \(\text{Alive}(j,T+1)\)​​​ 可以推到 \(\text{Dead}(j,T+1)\) 的 \(j\) 的数量​​​。

这样暴力做空间复杂度是平方的,时间复杂度是三方的,一脸过不去的样子,因此考虑优化,首先可以注意到一件事情就是我们其实并不需要缩点,因为在连出来的这张图中,只存在 \(\text{Alive}\to\text{Alive},\text{Dead}\to\text{Alive},\text{Dead}\to\text{Dead}\) 的边,并且 \(\text{Alive}\) 的边只会从时间后的连向时间前的,\(\text{Dead}\) 的边只会从时间前的连向时间后的,因此原图本身就是一个 DAG。其次,不难发现原图中很多点其实是没有用的,具体来说,我们只需要保留条件中的 \((x,t)\) 和 \((i,T+1)\),其他点都可以去掉,这样点数就降到了 \(\mathcal O(n+m)\),空间复杂度也就降了下来。

但是这样时间复杂度还是会爆炸,不过注意到此题我们只关心两个点之间的可达性,也就是一个取值只有 \(0/1\)​​​ 的布尔型变量,因此可以非常自然地想到 bitset,具体来说我们以每个 \((i,T+1)\)​​​ 为起点进行一边 DFS 找出它能够到达哪些 \(\text{Dead}(i,T+1)\)​​​,用一个 bitset 维护,每次转移就遍历与当前点相邻的点然后令该点的答案或上与其相连的点的答案即可,由于加了记忆化搜索,因此我们每个点最多计算一次,复杂度就是 \(\mathcal O(\dfrac{(n+m)^2}{\omega})\)​,但这样空间复杂度又爆掉了。因此考虑一个据说非常 common 的套路:分块 DFS,具体来说我们每 \(B\) 个元素一块,然后我们每次处理每一块中的 \(\text{Dead}(i,T+1)\) 对所有点的贡献,这样我们 bitset 的大小只用开到 \(B\),时间复杂度 \(\mathcal O(\dfrac{n^2}{\omega}+\dfrac{n^2}{B})\),空间复杂度 \(\dfrac{nB}{\omega}\),取 \(B=10^4\) 时最优。

const int MAXN=5e4;
const int MAXM=1e5;
const int MAXV=3e5;
const int MAXE=1e6;
const int BLK=1e4;
int n,m,T,ncnt;set<int> nd[MAXN+5];
struct data{int opt,t,x,y;} a[MAXM+5];
map<int,int> id[2][MAXN+5];
int hd[MAXV+5],nxt[MAXE+5],to[MAXE+5],ec=0;
void adde(int u,int v){to[++ec]=v;nxt[ec]=hd[u];hd[u]=ec;}
int lv[MAXN+5],dd[MAXN+5],bel[MAXV+5],res[MAXN+5];
bitset<MAXN+5> must;
bitset<MAXV+5> vis;
bitset<BLK+5> st[MAXV+5];
void dfs(int x,int l,int r){
if(vis.test(x)) return;vis.set(x);st[x].reset();
if(l<=bel[x]&&bel[x]<=r) st[x].set(bel[x]-l+1);
for(int e=hd[x];e;e=nxt[e]){
int y=to[e];dfs(y,l,r);
st[x]|=st[y];
}
}
int main(){
scanf("%d%d%d",&T,&n,&m);
for(int i=1;i<=m;i++) scanf("%d%d%d%d",&a[i].opt,&a[i].t,&a[i].x,&a[i].y);
for(int i=1;i<=n;i++) nd[i].insert(T+1);
for(int i=1;i<=m;i++) nd[a[i].x].insert(a[i].t);
for(int i=1;i<=n;i++){
for(int x:nd[i]) id[0][i][x]=++ncnt,id[1][i][x]=++ncnt;
int pre0=0,pre1=0;
for(int x:nd[i]){
int cur0=id[0][i][x],cur1=id[1][i][x];
if(pre0) adde(pre0,cur0),adde(cur1,pre1);
pre0=cur0;pre1=cur1;
}
}
for(int i=1;i<=m;i++){
if(!a[i].opt){
int tt=*nd[a[i].y].upper_bound(a[i].t);
adde(id[0][a[i].x][a[i].t],id[0][a[i].y][tt]);
adde(id[1][a[i].y][tt],id[1][a[i].x][a[i].t]);
} else {
int tt=*nd[a[i].y].lower_bound(a[i].t);
adde(id[1][a[i].x][a[i].t],id[0][a[i].y][tt]);
adde(id[1][a[i].y][tt],id[0][a[i].x][a[i].t]);
}
}
for(int i=1;i<=n;i++){
lv[i]=id[1][i][T+1];
dd[i]=id[0][i][T+1];bel[dd[i]]=i;
}
for(int l=1,r=BLK;l<=n;l+=BLK,r+=BLK){
chkmin(r,n);vis.reset();bitset<BLK+5> cur;
for(int i=1;i<=n;i++) dfs(lv[i],l,r);
for(int i=l;i<=r;i++) if(st[lv[i]].test(i-l+1)) must.set(i),cur.set(i-l+1);
for(int i=1;i<=n;i++) res[i]+=r-l+1-(st[lv[i]]|cur).count();
}
for(int i=1;i<=n;i++) printf("%d%c",(must.test(i))?0:(res[i]-1)," \n"[i==n]);
return 0;
}

洛谷 P5332 - [JSOI2019]精准预测(2-SAT+bitset+分块处理)的相关教程结束。

《洛谷 P5332 - [JSOI2019]精准预测(2-SAT+bitset+分块处理).doc》

下载本文的Word格式文档,以方便收藏与打印。