CF538H Summer Dichotomy 二分图、扫描线、线段树

标算太NB

我觉得用这种做法,这道题难度只有2500


如果原图不是二分图显然无解。否则对于一个点数\(\geq 2\)的连通块的两边可以缩成两个点,它们不能染相同的颜色。对于独立的点可以新建一个\([0,10^9]\)的虚点让这个点和虚点不染相同颜色。然后就变成了有\(n\)个区间、\(\frac{n}{2}\)对限制,每个区间仅在一对限制中。

枚举颜色\(1\)中所有区间的交,这样对于每一对限制连接的两个区间就会有一些限制。

举个栗子:对于限制\([l_1,r_1],[l_2,r_2]\),假设\(l_1,其余情况也是类似的推。假设当前枚举的交为\(p\)

  • 如果\(p\(p>r_2\)肯定没戏;

  • 如果\(l_1 \leq p < l_2\),那么区间\([l_2,r_2]\)一定要染颜色\(2\)

  • 如果\(r_1 < p \leq r_2\),那么区间\([l_1,r_1]\)一定要染颜色\(2\)

  • 如果\(l_2 \leq p \leq r_1\),那么这两个区间都可以染颜色\(1\),因为有一个必须染颜色\(2\),可以认为是区间\([l_1,r_2]\)一定要染颜色\(2\),然后根据最后选择了哪个位置选择究竟谁染颜色\(2\)

那么可以得到\(\frac{n}{2}\)个必须要染颜色\(2\)的区间(如果某个限制没戏就是空区间)。对它们取交并检查是否存在位置与\(p\)的和\(\in [t,T]\)。如果存在就找到了一组解。取交操作可以使用动态开点线段树区间加区间\(\max\)

直接枚举效率太低,可以发现从小到大枚举\(p\)会使得某一个限制中必须要染颜色\(2\)的区间改变的改变点只有\(O(n)\)种,所以离散化后扫描线即可。

#include
using namespace std;

int read(){
    int a = 0; char c = getchar(); while(!isdigit(c)) c = getchar();
    while(isdigit(c)){a = a * 10 + c - 48; c = getchar();} return a;
}

const int _ = 1e5 + 7 , __ = _ * 40;
#define mid ((l + r) >> 1)
int ch[__][2] , mrk[__] , cnt; pair < int , int > mx[__];
void mark(int x , int v){mrk[x] += v; mx[x].first += v;}
void modify(int &x , int l , int r , int L , int R , int val){
    if(!x){x = ++cnt; mx[x].second = l;}
    if(l >= L && r <= R) return mark(x , val);
    if(mid >= L) modify(ch[x][0] , l , mid , L , R , val);
    if(mid < R) modify(ch[x][1] , mid + 1 , r , L , R , val);
    mx[x] = max(mx[ch[x][0]] , mx[ch[x][1]]); mx[x].first += mrk[x];
}

pair < int , int > qry(int x , int l , int r , int L , int R){
    if(l >= L && r <= R) return make_pair(mx[x].first , mx[x].second == 0 ? l : mx[x].second);
    pair < int , int > mx(0 , 0); if(mid >= L) mx = qry(ch[x][0] , l , mid , L , R);
    if(mid < R) mx = max(mx , qry(ch[x][1] , mid + 1 , r , L , R));
    return make_pair(mx.first + mrk[x] , mx.second);
}

int t , T , N , M , col[_] , l[_] , r[_]; vector < int > nxt[_];
void dfs(int x , int c , vector < int > &L , vector < int > &R){
    col[x] = c; (!c ? L : R).push_back(x);
    for(auto t : nxt[x])
        if(col[t] == c){puts("IMPOSSIBLE"); exit(0);}
        else if(col[t] == -1) dfs(t , c ^ 1 , L , R);
}

struct mdy{int pos , l , r , val;}; vector < mdy > now;
void push(int p , int l , int r , int v){now.push_back((mdy){p , l , r , v});}

int main(){
    t = read(); T = read(); N = read(); M = read(); memset(col , -1 , sizeof(col));
    for(int i = 1 ; i <= N ; ++i){l[i] = read(); r[i] = read();}
    for(int i = 1 ; i <= M ; ++i){int x = read() , y = read(); nxt[x].push_back(y); nxt[y].push_back(x);}

    int num = 0;
    for(int i = 1 ; i <= N ; ++i)
        if(col[i] == -1){
            int l1 = 0 , r1 = 1e9 , l2 = 0 , r2 = 1e9;
            vector < int > L , R; dfs(i , 0 , L , R); ++num;
            for(auto t : L){l1 = max(l1 , l[t]); r1 = min(r1 , r[t]);}
            for(auto t : R){l2 = max(l2 , l[t]); r2 = min(r2 , r[t]);}
            if(l1 > l2 || l1 == l2 && r1 < r2){swap(l1 , l2); swap(r1 , r2);}
            if(l1 > r1 || l2 > r2){puts("IMPOSSIBLE"); return 0;}
            if(r2 <= r1){
                push(l1 , l2 , r2 , 1); push(l2 , l2 , r2 , -1); push(l2 , l1 , r1 , 1);
                push(r2 + 1 , l1 , r1 , -1); push(r2 + 1 , l2 , r2 , 1); push(r1 + 1 , l2 , r2 , -1);
            }
            else if(l2 <= r1){
                push(l1 , l2 , r2 , 1); push(l2 , l2 , r2 , -1); push(l2 , l1 , r2 , 1);
                push(r1 + 1 , l1 , r2 , -1); push(r1 + 1 , l1 , r1 , 1); push(r2 + 1 , l1 , r1 , -1);
            }
            else{
                push(l1 , l2 , r2 , 1); push(r1 + 1 , l2 , r2 , -1);
                push(l2 , l1 , r1 , 1); push(r2 + 1 , l1 , r1 , -1);
            }
        }

    sort(now.begin() , now.end() , [&](mdy x , mdy y){return x.pos < y.pos;});
    int pos = 0 , rt = 0 , P1 = -1 , P2 = -1; now.push_back((mdy){T + 1 , 0 , 0 , 0});
    while(pos < now.size() && now[pos].pos <= T){
        int p1 = pos;
        while(now[p1].pos == now[pos].pos){modify(rt , 0 , 1e9 , now[pos].l , now[pos].r , now[pos].val); ++pos;}
        int L = max(0 , t - now[pos].pos + 1) , R = T - now[p1].pos;
        pair < int , int > mx = qry(1 , 0 , 1e9 , L , R);
        if(mx.first == num){P1 = mx.second; P2 = min(now[pos].pos - 1 , T - P1); break;}
    }

    if(P1 == -1) puts("IMPOSSIBLE");
    else{
        puts("POSSIBLE"); printf("%d %d\n" , P1 , P2);
        memset(col , -1 , sizeof(col));
        for(int i = 1 ; i <= N ; ++i)
            if(col[i] == -1){
                int l1 = 0 , r1 = 1e9 , l2 = 0 , r2 = 1e9; vector < int > L , R; dfs(i , 0 , L , R);
                for(auto t : L){l1 = max(l1 , l[t]); r1 = min(r1 , r[t]);}
                for(auto t : R){l2 = max(l2 , l[t]); r2 = min(r2 , r[t]);}
                if(!(l1 <= P1 && r1 >= P1 && l2 <= P2 && r2 >= P2)){
                    for(auto t : L) col[t] ^= 1;
                    for(auto t : R) col[t] ^= 1;
                }
            }
        for(int i = 1 ; i <= N ; ++i) putchar(col[i] + '1');
    }
    return 0;
}

你可能感兴趣的:(CF538H Summer Dichotomy 二分图、扫描线、线段树)