题意

有一个\(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,所以可以统计线段树上的最小值的数量

代码

  1. /*
  2. 用线段树统计最小值的个数
  3. 就是在扩展的时候扩展到了一个点u
  4. 连边只连[l,u-1]之间的边
  5. 删除点l的时候只删除[l + 1 , r]的边
  6. */
  7. #include<vector>
  8. #include<cstdio>
  9. #include<cstring>
  10. #include<iostream>
  11. #include<algorithm>
  12. # define LL long long
  13. const int M = 200005 ;
  14. const int N = 2005 ;
  15. const int INF = 1e9 ;
  16. const int dx[] = {0 , 1 , 0 , -1} ;
  17. const int dy[] = {1 , 0 , -1 , 0} ;
  18. using namespace std ;
  19. inline int read() {
  20. char c = getchar() ; int x = 0 , w = 1 ;
  21. while(c>'9'||c<'0') { if(c=='-') w = -1 ; c = getchar() ; }
  22. while(c>='0'&&c<='9') { x = x*10+c-'0' ; c = getchar() ; }
  23. return x*w ;
  24. }
  25. LL ans ;
  26. int n , m , e , val[N][N] ;
  27. vector < int > eb[M] , ec[M] , vec ;
  28. struct Node { int tmin , cnt ; } ;
  29. inline Node chkmin(Node A , Node B) {
  30. Node c ; c.cnt = 0 ;
  31. c.tmin = min(A.tmin , B.tmin) ;
  32. if(c.tmin == A.tmin) c.cnt += A.cnt ;
  33. if(c.tmin == B.tmin) c.cnt += B.cnt ;
  34. return c ;
  35. }
  36. struct Link_Cut_Tree {
  37. # define ls (son[now][0])
  38. # define rs (son[now][1])
  39. int root , tot ;
  40. int fa[M] , son[M][2] , rev[M] , st[M] ;
  41. inline bool Nrt(int now) { return (son[fa[now]][0] == now || son[fa[now]][1] == now) ; }
  42. inline void Flip(int now) { swap(ls , rs) ; rev[now] ^= 1 ; }
  43. inline void pushdown(int now) {
  44. if(!rev[now]) return ;
  45. if(ls) Flip(ls) ; if(rs) Flip(rs) ;
  46. rev[now] = 0 ;
  47. }
  48. inline void rotate(int now) {
  49. int father = fa[now] , fafa = fa[father] , k = (son[father][1] == now) , w = son[now][k ^ 1] ;
  50. if(Nrt(father)) son[fafa][son[fafa][1] == father] = now ; son[now][k ^ 1] = father ; son[father][k] = w ;
  51. if(w) fa[w] = father ; fa[father] = now ; fa[now] = fafa ;
  52. }
  53. inline void splay(int now) {
  54. int top = 0 , father = now , fafa ; st[++top] = father ;
  55. while(Nrt(father)) father = fa[father] , st[++top] = father ;
  56. while(top) pushdown(st[top --]) ;
  57. while(Nrt(now)) {
  58. father = fa[now] , fafa = fa[father] ;
  59. if(Nrt(father)) rotate(((son[father][0] == now) ^ (son[fafa][0] == father)) ? now : father) ;
  60. rotate(now) ;
  61. }
  62. }
  63. inline void access(int now) {
  64. for(int ch = 0 ; now ; ch = now , now = fa[now])
  65. splay(now) , rs = ch ;
  66. }
  67. inline void makeroot(int now) {
  68. access(now) ; splay(now) ; Flip(now) ;
  69. }
  70. inline int findroot(int now) {
  71. access(now) ; splay(now) ;
  72. while(ls) pushdown(now) , now = ls ;
  73. return now ;
  74. }
  75. inline void Split(int x , int y) {
  76. makeroot(x) ; access(y) ; splay(y) ;
  77. }
  78. inline void Link(int x , int y) {
  79. makeroot(x) ;
  80. if(findroot(y) != x) fa[x] = y ;
  81. }
  82. inline void Cut(int x , int y) {
  83. makeroot(x) ;
  84. if(findroot(y) == x && !son[x][1] && fa[x] == y) fa[x] = son[y][0] = 0 ;
  85. }
  86. # undef ls
  87. # undef rs
  88. } lct ;
  89. struct Segment_Tree {
  90. # define ls (now << 1)
  91. # define rs (now << 1 | 1)
  92. int tmin[M << 2] , cnt[M << 2] , Tag[M << 2] ;
  93. inline void pushup(int now) {
  94. cnt[now] = 0 ;
  95. tmin[now] = min(tmin[ls] , tmin[rs]) ;
  96. if(tmin[ls] == tmin[now]) cnt[now] += cnt[ls] ;
  97. if(tmin[rs] == tmin[now]) cnt[now] += cnt[rs] ;
  98. }
  99. void build(int l , int r , int now) {
  100. tmin[now] = 0 ; cnt[now] = r - l + 1 ;
  101. if(l == r) return ; int mid = (l + r) >> 1 ;
  102. build(l , mid , ls) ; build(mid + 1 , r , rs) ;
  103. }
  104. inline void pushdown(int now) {
  105. if(!Tag[now]) return ;
  106. Tag[ls] += Tag[now] ; Tag[rs] += Tag[now] ;
  107. tmin[ls] += Tag[now] ; tmin[rs] += Tag[now] ;
  108. Tag[now] = 0 ;
  109. }
  110. void Change(int L , int R , int k , int l , int r , int now) {
  111. if(l >= L && r <= R) { tmin[now] += k ; Tag[now] += k ; return ; }
  112. pushdown(now) ; int mid = (l + r) >> 1 ;
  113. if(mid >= R) Change(L , R , k , l , mid , ls) ;
  114. else if(mid < L) Change(L , R , k , mid + 1 , r , rs) ;
  115. else Change(L , mid , k , l , mid , ls) , Change(mid + 1 , R , k , mid + 1 , r , rs) ;
  116. pushup(now) ;
  117. }
  118. Node query(int L , int R , int l , int r , int now) {
  119. if(l > R || r < L) return ((Node) { INF , 0 }) ;
  120. if(l >= L && r <= R) return ((Node) { tmin[now] , cnt[now] }) ;
  121. pushdown(now) ; int mid = (l + r) >> 1 ;
  122. if(mid >= R) return query(L , R , l , mid , ls) ;
  123. else if(mid < L) return query(L , R , mid + 1 , r , rs) ;
  124. else return chkmin(query(L , mid , l , mid , ls) , query(mid + 1 , R , mid + 1 , r , rs)) ;
  125. }
  126. # undef ls
  127. # undef rs
  128. } seg ;
  129. int main() {
  130. n = read() ; m = read() ; e = n * m ;
  131. for(int i = 1 ; i <= n ; i ++)
  132. for(int j = 1 ; j <= m ; j ++)
  133. val[i][j] = read() ;
  134. for(int i = 1 ; i <= n ; i ++)
  135. for(int j = 1 , x , y ; j <= m ; j ++)
  136. for(int k = 0 ; k < 4 ; k ++) {
  137. x = i + dx[k] , y = j + dy[k] ;
  138. if(x < 1 || y < 1 || x > n || y > m) continue ;
  139. if(val[x][y] > val[i][j])
  140. eb[val[i][j]].push_back(val[x][y]) ;
  141. else
  142. ec[val[i][j]].push_back(val[x][y]) ;
  143. }
  144. // eb[i] 连的边都是大于i的边
  145. // ec[i] 连的边都是小于i的边
  146. seg.build(1 , e , 1) ;
  147. int r = 0 ;
  148. for(int l = 1 ; l <= e ; l ++) {
  149. bool iscir = false ;
  150. for(int ri = r + 1 ; ri <= e ; ri ++) {
  151. vec.clear() ;
  152. for(int i = 0 , v ; i < ec[ri].size() ; i ++) {
  153. v = ec[ri][i] ; if(v < l) continue ;
  154. if(lct.findroot(ri) == lct.findroot(v)) {
  155. iscir = true ; break ;
  156. }
  157. lct.Link(ri , v) ;
  158. vec.push_back(v) ;
  159. }
  160. for(int i = 0 ; i < vec.size() ; i ++)
  161. lct.Cut(ri , vec[i]) ;
  162. if(iscir) break ;
  163. ++ r ; int tot = 0 ;
  164. for(int i = 0 , v ; i < ec[ri].size() ; i ++) {
  165. v = ec[ri][i] ; if(v < l) continue ;
  166. lct.Link(ri , v) ; ++ tot ;
  167. }
  168. seg.Change( ri , e , - tot , 1 , e , 1 ) ;
  169. seg.Change( ri , ri , r - l + 1 , 1 , e , 1 ) ;
  170. }
  171. Node temp = seg.query(l , r , 1 , e , 1) ;
  172. if(temp.tmin == 1) ans += temp.cnt ;
  173. for(int i = 0 , v ; i < eb[l].size() ; i ++) {
  174. v = eb[l][i] ; if(v > r) continue ;
  175. lct.Cut(l , v) ;
  176. seg.Change( v , e , 1 , 1 , e , 1 ) ;
  177. }
  178. seg.Change(l , r , -1 , 1 , e , 1) ;
  179. }
  180. printf("%lld\n",ans) ;
  181. return 0 ;
  182. }

[CF1109F]Sasha and Algorithm of Silence's Sounds的更多相关文章

  1. CF1109F Sasha and Algorithm of Silence's Sounds LCT、线段树

    传送门 构成一棵树可以分成两个限制:图不成环.图的点数-边数=1. 我们考虑枚举右端点\(r\)计算所有可能的左端点\(l\)的答案.我们先考虑第一个限制:图不成环.注意到当\(r\)确定的时候,满足 ...

  2. CodeForces 1109F. Sasha and Algorithm of Silence's Sounds

    题目简述:给定一个$n \times m$的二维矩阵$a[i][j]$,其中$1 \leq nm \leq 2 \times 10^5$,矩阵元素$1 \leq a[i][j] \leq nm$且互不 ...

  3. Codeforces Round #539 (Div. 1) 1109F. Sasha and Algorithm of Silence's Sounds LCT+线段树 (two pointers)

    题解请看 Felix-Lee的CSDN博客 写的很好,不过最后不用判断最小值是不是1,因为[i,i]只有一个点,一定满足条件,最小值一定是1. CODE 写完就A,刺激. #include <b ...

  4. Codeforces 1109F - Sasha and Algorithm of Silence's Sounds(LCT)

    Codeforces 题面传送门 & 洛谷题面传送门 讲个笑话,这题是 2020.10.13 dxm 讲题时的一道例题,而我刚好在一年后的今天,也就是 2021.10.13 学 LCT 时做到 ...

  5. Codeforces Round #539 Div1 题解

    Codeforces Round #539 Div1 题解 听说这场很适合上分QwQ 然而太晚了QaQ A. Sasha and a Bit of Relax 翻译 有一个长度为\(n\)的数组,问有 ...

  6. Codeforces Round #539&#542&#543&#545 (Div. 1) 简要题解

    Codeforces Round #539 (Div. 1) A. Sasha and a Bit of Relax description 给一个序列\(a_i\),求有多少长度为偶数的区间\([l ...

  7. 转 释一首美国民谣:沉默之音(The Sound Of Silence)

    Ask not what your country can do for you , ask what you can do for your country.    六十年代对美国而言是个多事之秋的 ...

  8. Silence Removal and End Point Detection MATLAB Code

    转载自:http://ganeshtiwaridotcomdotnp.blogspot.com/2011/08/silence-removal-and-end-point-detection.html ...

  9. 【Codeforces718C】Sasha and Array 线段树 + 矩阵乘法

    C. Sasha and Array time limit per test:5 seconds memory limit per test:256 megabytes input:standard ...

随机推荐

  1. ArcGIS Engine 中的绘制与编辑

    1.线段绘制 基本步骤 构建形状 1. 创建 IPoint IPoint m_Point = new PointClass(); m_Point.PutCoords(x, y); 2. 创建 IPoi ...

  2. Java入门 第一季第五章 编程练习解析

    这是我学习慕课网Java课程的笔记.原视频链接为:http://www.imooc.com/learn/85 5-1 基本写法 自己主动补全快捷键:alt + / 5-2 输入输出 使用Scanner ...

  3. git clone新项目后如何拉取其他分支代码到本地

    1.git clone git@git.n.xxx.com:xxx/xxx.git 2.git fetch origin dev 命令来把远程dev分支拉到本地 - - 解读:git fetch命令用 ...

  4. 从Script到Code Blocks、Code Behind到MVC、MVP、MVVM(转载)

    http://www.cnblogs.com/indream/p/3602348.html 刚过去的周五(3-14)例行地主持了技术会议,主题正好是<UI层的设计模式——从Script.Code ...

  5. Koa2学习(二)async/await

    Koa2学习(二)async/await koa2中用到了大量的async/await语法,要学习koa2框架,首先要好好理解async/await语法. async/await顾名思义是一个异步等待 ...

  6. linux php nginx php-fpm 关系 动态进程生成

    yum install php yum install php-fpm 启动fpm [root@VM_141_64_centos html]# service php-fpm restart Redi ...

  7. Lightoj 1009 - Back to Underworld

    1009 - Back to Underworld    PDF (English) Statistics Forum Time Limit: 4 second(s) Memory Limit: 32 ...

  8. sql server 支持中文繁体和简体

    SET ANSI_PADDING ON  INSERT INTO 表 VALUES (N'中文')

  9. ubuntu12.04 64位系统配置jdk1.6和jdk-6u20-linux-i586.bin下载地址

    1:下载地址http://code.google.com/p/autosetup1/downloads/detail?name=jdk-6u20-linux-i586.bin&can=2&am ...

  10. codeforces 686B B. Little Robber Girl's Zoo(水题)

    题目链接: B. Little Robber Girl's Zoo //#include <bits/stdc++.h> #include <vector> #include ...