【AT2336】Flags

链接:AT2336 Flags
(题意不解释了)

解法:2-sat + tarjan-scc + 线段树/分块

(话说2-sat这东西我读成二坐,zh大佬每次都要纠正我233333)

使用2-sat建模,将每个点 i i 拆为 i i i i ′ ,题目转化为选点问题。一条边 (u,v) ( u , v ) 表示若选择了 u u ,则 v v 必须被选择。这东西有啥用呢?由于输入的每对数要么必须选 x x ,要么必须选 y y ,也就是说若我们能用2-sat推出“选 u u 必须选 u u ′ ”那么问题解决。

考虑如何连边。首先二分答案,设答案为 m m ,那么我们用线段树优化连边(或使用分块优化连边),将所有读入的值排序,对于一个点 u u ,在排序的值中二分查找区间 [l,r] [ l , r ] ,让 [l,r] [ l , r ] 上的每个数与 u u 的相对差均小于 m m ,然后由于二分出的答案为 m m ,所以若 [l,r] [ l , r ] 区间上的选了,则 u u 不能选,从而 u u ′ 必须选,于是将 [l,r] [ l , r ] 区间上的每个点向 u u ′ 连边。

判断时,先求出所有强连通分量,若某个点 u u u u ′ 处于同一强连通分量内,那么即是说:若 u u 选了,则 u u ′ 必须选,与题设矛盾,验证返回伪。

时间复杂度:线段树连边 O(Nlog2N) O ( N log 2 ⁡ N ) ,分块连边 O(NNlogN) O ( N N log ⁡ N ) ,均可过。

代码(只有线段树版本的,我太懒了QAQ)

#include
#include
#include
#include
#include
#include

using namespace std;

struct data{
    int x,i;
    data():x(0),i(0){}
    data(int y,int j):x(y),i(j){}
    friend bool operator<(const data &dt1,const data &dt2){return dt1.xstack<int> s;
int n,x[80001],idx,tot,id[80001],low[80001],dfn[80001],scc,cid[80001];
vector vec;
vector<int> point[80001];

void build(int o,int l,int r){
    id[o]=++tot;
    if(o>1)point[id[o>>1]].push_back(tot);
    if(l==r){int v=vec[l-1].i;point[id[o]].push_back(v<=n?v+n:v-n);return;}
    int m=l+r>>1;
    build(o<<1,l,m),build(o<<1|1,m+1,r);
}

void update(int o,int l,int r,int a,int b,int x){
    if(a>b)return;
    if(l==a&&r==b){point[x].push_back(id[o]);return;}
    int m=l+r>>1;
    if(m>=b)update(o<<1,l,m,a,b,x);else if(m1|1,m+1,r,a,b,x);else update(o<<1,l,m,a,m,x),update(o<<1|1,m+1,r,m+1,b,x);
}

pair<int,int> get(int i,int m){
    pair<int,int> res;int l=1,r=i,mid;
    while(l<=r)if(vec[i-1].x-vec[(mid=l+r>>1)-1].x>=m)l=mid+1;else r=mid-1;
    res.first=r+1,l=i,r=n<<1;
    while(l<=r)if(vec[(mid=l+r>>1)-1].x-vec[i-1].x1;else r=mid-1;
    res.second=l-1;
    return res;
}

void dfs(int u){
    low[u]=dfn[u]=++idx;s.push(u);
    for(int v:point[u])if(!dfn[v]){dfs(v);low[u]=min(low[u],low[v]);}else if(dfn[v]>0)low[u]=min(low[u],dfn[v]);
    if(low[u]==dfn[u]){int x;++scc;while(x!=u)x=s.top(),s.pop(),dfn[x]=-dfn[x],cid[x]=scc;}
}

bool check(int m){
    memset(low,scc=idx=0,sizeof(low)),memset(dfn,0,sizeof(dfn));for(int i=1;i<=80000;++i)point[i].clear();
    build(1,1,tot=n<<1);
    for(int i=1;i<=n<<1;++i){int r=vec[i-1].i;pair<int,int> pr=get(i,m);update(1,1,n<<1,pr.first,i-1,r),update(1,1,n<<1,i+1,pr.second,r);}
    for(int i=1;i<=n<<1;++i)if(!dfn[i])dfs(i);
    for(int i=1;i<=n;++i)if(cid[i]==cid[i+n])return false;
    return true;
}

int main(){
    scanf("%d",&n);
    for(int i=1;i<=n;++i)scanf("%d%d",x+i,x+n+i),vec.push_back(data(x[i],i)),vec.push_back(data(x[i+n],i+n));
    sort(vec.begin(),vec.end());
    int l=0,r=1000000000,m;while(l<=r)if(check(m=l+r>>1))l=m+1;else r=m-1;
    printf("%d",l-1);
}

你可能感兴趣的:(2-sat,连通分量,建模优化)