1. 程式人生 > >[CF1109F]Sasha and Algorithm of Silence's Sounds

[CF1109F]Sasha and Algorithm of Silence's Sounds

不能 pan cst 統計 swap 隨著 ios ack tin

題意

有一個\(n*m\)的網格,每個格子有一個數,為\(1\)~\(n * m\)的排列
一個區間\((1<=l<=r<=n*m)\)是好的,當且僅當:數值在該區間內的格子,構成一棵樹(有公共邊的格子)
統計好區間數
\(n,m<=2000,n*m<=2*10^5\)

題解

首先先考慮什麽情況形成了一棵樹?
顯然是 這個區間的點數 - 這個區間內互相連的邊數 = 1 且 只有一個聯通塊

這樣統計區間的問題一般都要雙指針掃一下
我們可以固定左指針\(l\),然後讓右指針\(r\)向右掃
顯然如果\([l,r]\)之間的邊構成了一個環,那麽就不能讓\(r\)往右移動,也就是保證所有的區間都不存在環

顯然隨著\(l\)的增加\(r\)不會減小

那麽我們就可以對於每一個\(l\)都統計出ta所對應的最大的不存在環的連續區間能到哪裏,也就是\(r\),這一步可以用\(LCT\)完成

現在我們的問題就是怎麽快速的統計每一個合法的區間\([l,i](l\le i \le r)\)
我們已經可以確定這段區間是沒有環的了
所以只需要統計哪些區間的 點數 - 邊數 = 1 就好了(這個邊數指的是在區間內的邊)
這玩意兒怎麽統計?
可以發現線段樹上每個合法區間的節點的最小值就是1,所以可以統計線段樹上的最小值的數量

代碼

/*
用線段樹統計最小值的個數 
就是在擴展的時候擴展到了一個點u
連邊只連[l,u-1]之間的邊
刪除點l的時候只刪除[l + 1 , r]的邊 

*/
#include<vector>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
# define LL long long
const int M = 200005 ;
const int N = 2005 ;
const int INF = 1e9 ;
const int dx[] = {0 , 1 , 0 , -1} ;
const int dy[] = {1 , 0 , -1 , 0} ;
using namespace std ;

inline int read() {
    char c = getchar() ; int x = 0 , w = 1 ;
    while(c>'9'||c<'0') { if(c=='-') w = -1 ; c = getchar() ; }
    while(c>='0'&&c<='9') { x = x*10+c-'0' ; c = getchar() ; }
    return x*w ;
}

LL ans ;
int n , m , e , val[N][N] ; 
vector < int > eb[M] , ec[M] , vec ; 
struct Node {  int tmin , cnt ; } ;
inline Node chkmin(Node A , Node B) {
    Node c ; c.cnt = 0 ;
    c.tmin = min(A.tmin , B.tmin) ;
    if(c.tmin == A.tmin) c.cnt += A.cnt ;
    if(c.tmin == B.tmin) c.cnt += B.cnt ;
    return c ;
}
struct Link_Cut_Tree {
    # define ls (son[now][0])
    # define rs (son[now][1])
    int root , tot ;
    int fa[M] , son[M][2] , rev[M] , st[M] ;
    inline bool Nrt(int now) { return (son[fa[now]][0] == now || son[fa[now]][1] == now) ; }
    inline void Flip(int now) { swap(ls , rs) ; rev[now] ^= 1 ; }
    inline void pushdown(int now) {
        if(!rev[now]) return ;
        if(ls) Flip(ls) ; if(rs) Flip(rs) ;
        rev[now] = 0 ;
    }
    inline void rotate(int now) {
        int father = fa[now] , fafa = fa[father] , k = (son[father][1] == now) , w = son[now][k ^ 1] ;
        if(Nrt(father)) son[fafa][son[fafa][1] == father] = now ; son[now][k ^ 1] = father ; son[father][k] = w ;
        if(w) fa[w] = father ; fa[father] = now ; fa[now] = fafa ;
    }
    inline void splay(int now) {
        int top = 0 , father = now , fafa ; st[++top] = father ;
        while(Nrt(father)) father = fa[father] , st[++top] = father ;
        while(top) pushdown(st[top --]) ;
        while(Nrt(now)) {
            father = fa[now] , fafa = fa[father] ;
            if(Nrt(father)) rotate(((son[father][0] == now) ^ (son[fafa][0] == father)) ? now : father) ;
            rotate(now) ;
        }
    }
    inline void access(int now) {
        for(int ch = 0 ; now ; ch = now , now = fa[now])
            splay(now) , rs = ch ;
    }
    inline void makeroot(int now) {
        access(now) ; splay(now) ; Flip(now) ;
    }
    inline int findroot(int now) {
        access(now) ; splay(now) ;
        while(ls) pushdown(now) , now = ls ;
        return now ;
    }
    inline void Split(int x , int y) {
        makeroot(x) ; access(y) ; splay(y) ;
    }
    inline void Link(int x , int y) {
        makeroot(x) ;
        if(findroot(y) != x) fa[x] = y ;
    }
    inline void Cut(int x , int y) {
        makeroot(x) ;
        if(findroot(y) == x && !son[x][1] && fa[x] == y) fa[x] = son[y][0] = 0 ;
    }
    # undef ls
    # undef rs
} lct ;
struct Segment_Tree {
    # define ls (now << 1)
    # define rs (now << 1 | 1)
    int tmin[M << 2] , cnt[M << 2] , Tag[M << 2] ;
    inline void pushup(int now) {
        cnt[now] = 0 ;
        tmin[now] = min(tmin[ls] , tmin[rs]) ;
        if(tmin[ls] == tmin[now]) cnt[now] += cnt[ls] ;
        if(tmin[rs] == tmin[now]) cnt[now] += cnt[rs] ;
    }
    void build(int l , int r , int now) {
        tmin[now] = 0 ; cnt[now] = r - l + 1 ;
        if(l == r) return ; int mid = (l + r) >> 1 ;
        build(l , mid , ls) ; build(mid + 1 , r , rs) ;
    }
    inline void pushdown(int now) {
        if(!Tag[now]) return ;
        Tag[ls] += Tag[now] ; Tag[rs] += Tag[now] ;
        tmin[ls] += Tag[now] ; tmin[rs] += Tag[now] ;
        Tag[now] = 0 ;
    }
    void Change(int L , int R , int k , int l , int r , int now) {
        if(l >= L && r <= R) { tmin[now] += k ; Tag[now] += k ; return ; }
        pushdown(now) ; int mid = (l + r) >> 1 ;
        if(mid >= R) Change(L , R , k , l , mid , ls) ;
        else if(mid < L) Change(L , R , k , mid + 1 , r , rs) ;
        else Change(L , mid , k , l , mid , ls) , Change(mid + 1 , R , k , mid + 1 , r , rs) ;
        pushup(now) ;
    }
    Node query(int L , int R , int l , int r , int now) {
        if(l > R || r < L) return ((Node) { INF , 0 }) ;
        if(l >= L && r <= R) return ((Node) { tmin[now] , cnt[now] }) ;
        pushdown(now) ; int mid = (l + r) >> 1 ;
        if(mid >= R) return query(L , R , l , mid , ls) ;
        else if(mid < L) return query(L , R , mid + 1 , r , rs) ;
        else return chkmin(query(L , mid , l , mid , ls) , query(mid + 1 , R , mid + 1 , r , rs)) ;
    }
    # undef ls
    # undef rs
} seg ;

int main() {
    n = read() ; m = read() ; e = n * m ; 
    for(int i = 1 ; i <= n ; i ++)
        for(int j = 1 ; j <= m ; j ++)
            val[i][j] = read() ;
    for(int i = 1 ; i <= n ; i ++)
        for(int j = 1 , x , y ; j <= m ; j ++)
            for(int k = 0 ; k < 4 ; k ++) {
                x = i + dx[k] , y = j + dy[k] ;
                if(x < 1 || y < 1 || x > n || y > m) continue ;
                if(val[x][y] > val[i][j]) 
                    eb[val[i][j]].push_back(val[x][y]) ;
                else 
                    ec[val[i][j]].push_back(val[x][y]) ;
            }
// eb[i] 連的邊都是大於i的邊 
// ec[i] 連的邊都是小於i的邊     
    seg.build(1 , e , 1) ;
    int r = 0 ;
    for(int l = 1 ; l <= e ; l ++) {
        bool iscir = false ;
        for(int ri = r + 1 ; ri <= e ; ri ++) {
            vec.clear() ;
            for(int i = 0 , v ; i < ec[ri].size() ; i ++) {
                v = ec[ri][i] ; if(v < l) continue ;
                if(lct.findroot(ri) == lct.findroot(v)) {
                    iscir = true ; break ;
                }
                lct.Link(ri , v) ;
                vec.push_back(v) ;
            }
            for(int i = 0 ; i < vec.size() ; i ++) 
                lct.Cut(ri , vec[i]) ;
            if(iscir) break ;
            ++ r ; int tot = 0 ;
            for(int i = 0 , v ; i < ec[ri].size() ; i ++) {
                v = ec[ri][i] ; if(v < l) continue ;
                lct.Link(ri , v) ; ++ tot ;
            }
            seg.Change( ri , e , - tot , 1 , e , 1 ) ;
            seg.Change( ri , ri , r - l + 1 , 1 , e , 1 ) ;
        }
        Node temp = seg.query(l , r , 1 , e , 1) ;
        if(temp.tmin == 1) ans += temp.cnt ;
        for(int i = 0 , v ; i < eb[l].size() ; i ++) {
            v = eb[l][i] ; if(v > r) continue ;
            lct.Cut(l , v) ;
            seg.Change( v , e , 1 , 1 , e , 1 ) ;
        }
        seg.Change(l , r , -1 , 1 , e , 1) ;
    }
    printf("%lld\n",ans) ;
    return 0 ;
}

[CF1109F]Sasha and Algorithm of Silence's Sounds