• [CF1109F]Sasha and Algorithm of Silence's Sounds


    题意

    有一个(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](lle 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
    ",ans) ;
        return 0 ;
    }
    
  • 相关阅读:
    常见问题
    查询
    多对多关系
    prototype & __proto__
    new operator
    用户
    express.Router
    Express 应用生成器
    LeanCloud
    npm常用命令
  • 原文地址:https://www.cnblogs.com/beretty/p/10633304.html
Copyright © 2020-2023  润新知