【2019北京集训3】逻辑 树剖+2-sat

时间:2023-03-09 15:42:23
【2019北京集训3】逻辑 树剖+2-sat

题目大意:有一颗有$m$个叶子节点的二叉树。

对于叶子节点$i$,$x[i]=(a[i]\ xor\ V_{p[i]})or(b[i]\ xor\ V_{q[i]})$

对于非叶子节点$i$,$x[i]=x[sonl]\ and\ x[sonr]$。

上文的$or$和$xor$均为逻辑运算符。且V为一个长度为$n$的布尔数组,需要你自己构造。

下面问:对于每个非叶子节点$i$,问是否存在一个序列V,使得$x[i]=true$。

数据范围:$n,m≤2\times 10^{5}$

我们先来考虑下暴力应该怎么做

我们直接暴力将以$i$为根的子树内所有叶节点取出,转换出对应的了逻辑表达式,然后根据2-sat那一套建图,直接tarjan即可。

这么搞,数据优秀的话(给你一个毛毛虫图),你就会爆炸

考虑一种不会爆炸的做法:

我们对这棵树跑一次树剖,我们根据链长依次在链上进行二分,查找出最接近根的,能够构造出true的点。

然后冷静分析一波,发现这个复杂度好像是$O(n\log^2\ n)$的,似乎问题不大。

可是问题在于这一题采用$O(n^2)$的做法可以直接艹过此题,我就并没有去写高级做法了qwq

 #include<bits/stdc++.h>
#define M 500005
using namespace std; int n,m,rt=;
int p[M]={},q[M]={},A[M]={},B[M]={},in[M]={}; vector<int> G[M]; void ReadData(){
scanf("%d%d",&n,&m);
for(int i=;i<=m;i++){
int P,Q; scanf("%d%d",&P,&Q);
p[i]=abs(P); q[i]=abs(Q);
A[i]=(P<); B[i]=(Q<);
}
for(int i=m+;i<m*;i++){
int x,y; scanf("%d%d",&x,&y);
G[i].push_back(x);
G[i].push_back(y);
in[x]++; in[y]++;
}
for(int i=;i<m*;i++)
if(in[i]==){rt=i;}
} struct edge{int u,next;}e[M*]={}; int head[M]={},use=;
void add(int x,int y){use++;e[use].u=y;e[use].next=head[x];head[x]=use;}
int dfn[M]={},low[M]={},b[M]={},d[M]={},cnt=,t=; stack<int> s;
void dfs(int x){
dfn[x]=low[x]=++t; b[x]=; s.push(x);
for(int i=head[x];i;i=e[i].next)
if(!dfn[e[i].u]) dfs(e[i].u),low[x]=min(low[x],low[e[i].u]);
else if(b[e[i].u]) low[x]=min(low[x],dfn[e[i].u]);
if(dfn[x]==low[x]){
cnt++; int u;
do{
u=s.top(); s.pop();
b[u]=; d[u]=cnt;
}while(u!=x);
}
} vector<int> List;
void findpoint(int x){
int X;
X=p[x]; dfn[X]=d[X]=head[X]=;
X+=n; dfn[X]=d[X]=head[X]=;
X=q[x]; dfn[X]=d[X]=head[X]=;
X+=n; dfn[X]=d[X]=head[X]=;
if(G[x].size()==){
List.push_back(x);
return;
}
for(int i=;i<G[x].size();i++)
findpoint(G[x][i]);
} bool check(int x){
List.clear();
use=cnt=t=;
findpoint(x);
for(int i=;i<List.size();i++){
int v = List[i];
int rtid = q[v], lftid = p[v];
int lft = A[v], rt = B[v];
add(rtid+rt*n,lftid+(-lft)*n);
add(lftid+lft*n,rtid+(-rt)*n);
}
for(int i=;i<List.size();i++){
int now=List[i];
int u=p[now],v=q[now];
if(!dfn[u]) dfs(u);
if(!dfn[v]) dfs(v);
}
for(int i=;i<List.size();i++){
int now=List[i];
int u=p[now],v=q[now];
if(d[u]==d[u+n]) return ;
if(d[v]==d[v+n]) return ;
}
return ;
} int ans[M]={};
bool solve(int x,int ok){
if(G[x].size()==) return ;
if(!ok) ok=check(x);
if(ok) ans[x-m]=;
solve(G[x][],ok); solve(G[x][],ok);
} int main(){
ReadData();
solve(rt,);
for(int i=;i<m;i++) printf("%d",ans[i]);
}