[CF1109F]Sasha and Algorithm of Silence's Sounds
阿新 • • 發佈:2019-04-01
不能 pan cst 統計 swap 隨著 ios ack tin
顯然隨著\(l\)的增加\(r\)不會減小
題意
有一個\(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\)都統計出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