题目链接:CF 或者 洛谷

析合树真是连续段问题的降智神器

先看下题目的一些特殊性,每行每列恰好有一个棋子。考虑特殊性,\(n \times n\) 的棋盘,那么就该判断是否有 \(n\) 个棋子,容易观察到,也就是相当于每一行并且每一列都有一个棋子。而容易知道,这些棋子所在的行或者列拿出来应当是“连续的”。

容易知道一个事实,如果你的行是连续的,并且列是连续的,那么一定能够组成答案,但如果只满足其中一个,就不一定了:

比如如图所示,这三个黑色格子是属于行连续的,但列并不连续,而它们并不能组成答案。同理,列连续但行不连续也无法组成答案。

我们得到了本题的一个贡献的充要条件,行需要连续,列也需要连续,但这个二维问题比较难以解决,我们考虑让某一维先连续,即比如以行为关键字进行排序,这样一来任意一段都是行连续了,这个时候在来考虑列连续性即可。当然了也可以这样去写:\(idx[row]=col\),这样一来,自然按照行排好序了,因为这本质是排列,并不会出现重复数字,也是排列问题常用的一种排序方式。

接下来就转变为了一个数组上,有多少个连续段区间的问题,线段树确实处理起来比较需要思维难度,但析合树解决起来就不需要太多思考。

  1. 如果某个点是析点,显然贡献为 \(1\),只有它的所有儿子节点合并起来才是连续段。

  2. 如果某个点是合点,显然贡献为 \(siz \choose 2\),因为它的任意两个儿子节点都可以组成一个新的连续段,所以应该为任取两个儿子的方案数。

那么只需要建析合树,然后遍历一遍树节点就行了。

参照代码
#include <bits/stdc++.h>

// #pragma GCC optimize("Ofast,unroll-loops")
// #pragma GCC optimize(2) // #define isPbdsFile #ifdef isPbdsFile #include <bits/extc++.h> #else #include <ext/pb_ds/priority_queue.hpp>
#include <ext/pb_ds/hash_policy.hpp>
#include <ext/pb_ds/tree_policy.hpp>
#include <ext/pb_ds/trie_policy.hpp>
#include <ext/pb_ds/tag_and_trait.hpp>
#include <ext/pb_ds/hash_policy.hpp>
#include <ext/pb_ds/list_update_policy.hpp>
#include <ext/pb_ds/assoc_container.hpp>
#include <ext/pb_ds/exception.hpp>
#include <ext/rope> #endif using namespace std;
using namespace __gnu_cxx;
using namespace __gnu_pbds;
typedef long long ll;
typedef long double ld;
typedef pair<int, int> pii;
typedef pair<ll, ll> pll;
typedef tuple<int, int, int> tii;
typedef tuple<ll, ll, ll> tll;
typedef unsigned int ui;
typedef unsigned long long ull;
typedef __int128 i128;
#define hash1 unordered_map
#define hash2 gp_hash_table
#define hash3 cc_hash_table
#define stdHeap std::priority_queue
#define pbdsHeap __gnu_pbds::priority_queue
#define sortArr(a, n) sort(a+1,a+n+1)
#define all(v) v.begin(),v.end()
#define yes cout<<"YES"
#define no cout<<"NO"
#define Spider ios_base::sync_with_stdio(false);cin.tie(nullptr);cout.tie(nullptr);
#define MyFile freopen("..\\input.txt", "r", stdin),freopen("..\\output.txt", "w", stdout);
#define forn(i, a, b) for(int i = a; i <= b; i++)
#define forv(i, a, b) for(int i=a;i>=b;i--)
#define ls(x) (x<<1)
#define rs(x) (x<<1|1)
#define endl '\n'
//用于Miller-Rabin
[[maybe_unused]] static int Prime_Number[13] = {0, 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37}; template <typename T>
int disc(T* a, int n)
{
return unique(a + 1, a + n + 1) - (a + 1);
} template <typename T>
T lowBit(T x)
{
return x & -x;
} template <typename T>
T Rand(T l, T r)
{
static mt19937 Rand(time(nullptr));
uniform_int_distribution<T> dis(l, r);
return dis(Rand);
} template <typename T1, typename T2>
T1 modt(T1 a, T2 b)
{
return (a % b + b) % b;
} template <typename T1, typename T2, typename T3>
T1 qPow(T1 a, T2 b, T3 c)
{
a %= c;
T1 ans = 1;
for (; b; b >>= 1, (a *= a) %= c)if (b & 1)(ans *= a) %= c;
return modt(ans, c);
} template <typename T>
void read(T& x)
{
x = 0;
T sign = 1;
char ch = getchar();
while (!isdigit(ch))
{
if (ch == '-')sign = -1;
ch = getchar();
}
while (isdigit(ch))
{
x = (x << 3) + (x << 1) + (ch ^ 48);
ch = getchar();
}
x *= sign;
} template <typename T, typename... U>
void read(T& x, U&... y)
{
read(x);
read(y...);
} template <typename T>
void write(T x)
{
if (typeid(x) == typeid(char))return;
if (x < 0)x = -x, putchar('-');
if (x > 9)write(x / 10);
putchar(x % 10 ^ 48);
} template <typename C, typename T, typename... U>
void write(C c, T x, U... y)
{
write(x), putchar(c);
write(c, y...);
} template <typename T11, typename T22, typename T33>
struct T3
{
T11 one;
T22 tow;
T33 three; bool operator<(const T3 other) const
{
if (one == other.one)
{
if (tow == other.tow)return three < other.three;
return tow < other.tow;
}
return one < other.one;
} T3() { one = tow = three = 0; } T3(T11 one, T22 tow, T33 three) : one(one), tow(tow), three(three)
{
}
}; template <typename T1, typename T2>
void uMax(T1& x, T2 y)
{
if (x < y)x = y;
} template <typename T1, typename T2>
void uMin(T1& x, T2 y)
{
if (x > y)x = y;
} constexpr int N = 6e5 + 10;
constexpr int T = 25;
int idx[N];
int n; struct
{
int MAX[N][T], MIN[N][T];
int LOG2[N]; void build()
{
forn(i, 2, n)LOG2[i] = LOG2[i >> 1] + 1;
const int k = LOG2[n] + 1;
forn(i, 1, n)MAX[i][0] = MIN[i][0] = idx[i];
forn(j, 1, k)
{
forn(i, 1, n-(1<<j)+1)
{
MAX[i][j] = max(MAX[i][j - 1], MAX[i + (1 << j - 1)][j - 1]);
MIN[i][j] = min(MIN[i][j - 1], MIN[i + (1 << j - 1)][j - 1]);
}
}
} int query(const int l, const int r) const
{
const int k = LOG2[r - l + 1];
return max(MAX[l][k], MAX[r - (1 << k) + 1][k]) - min(MIN[l][k], MIN[r - (1 << k) + 1][k]);
}
} ST; struct
{
struct Node
{
int Min, Add;
} node[N << 2]; #define Min(x) node[x].Min
#define Add(x) node[x].Add void push_up(const int curr)
{
Min(curr) = min(Min(ls(curr)),Min(rs(curr)));
} void push_down(const int curr)
{
if (Add(curr))
{
Min(ls(curr)) += Add(curr), Min(rs(curr)) += Add(curr);
Add(ls(curr)) += Add(curr), Add(rs(curr)) += Add(curr);
Add(curr) = 0;
}
} void add(const int curr, const int l, const int r, const int val, const int s = 1, const int e = n)
{
if (l <= s and e <= r)
{
Min(curr) += val, Add(curr) += val;
return;
}
const int mid = s + e >> 1;
push_down(curr);
if (l <= mid)add(ls(curr), l, r, val, s, mid);
if (r > mid)add(rs(curr), l, r, val, mid + 1, e);
push_up(curr);
} int query(const int curr = 1, const int l = 1, const int r = n)
{
if (l == r)return l;
const int mid = l + r >> 1;
push_down(curr);
if (!Min(ls(curr)))return query(ls(curr), l, mid);
return query(rs(curr), mid + 1, r);
}
} Seg; struct DCT
{
int left, right, type, rightL; DCT(const int left, const int right, const int type = 0, const int right_l = 0)
: left(left),
right(right),
type(type),
rightL(right_l)
{
} DCT() = default;
} dct[N]; inline bool isContinue(const int l, const int r)
{
return ST.query(l, r) == r - l;
} #define left(x) dct[x].left
#define right(x) dct[x].right
#define type(x) dct[x].type
#define rightL(x) dct[x].rightL
int cnt;
int stMax[N], stMin[N], maxCnt, minCnt;
#define maxTop idx[stMax[maxCnt]]
#define minTop idx[stMin[minCnt]]
int node[N];
vector<int> child[N];
stack<int> st;
#define root st.top()
int dctRoot; inline void build()
{
forn(i, 1, n)
{
while (maxCnt and idx[i] >= maxTop)Seg.add(1, stMax[maxCnt - 1] + 1, stMax[maxCnt], -maxTop), --maxCnt;
while (minCnt and idx[i] <= minTop)Seg.add(1, stMin[minCnt - 1] + 1, stMin[minCnt],minTop), --minCnt;
Seg.add(1, stMax[maxCnt] + 1, i, idx[i]), Seg.add(1, stMin[minCnt] + 1, i, -idx[i]);
stMax[++maxCnt] = stMin[++minCnt] = i;
int curr;
dct[curr = node[i] = ++cnt] = DCT(i, i);
const int L = Seg.query();
while (!st.empty() and left(root) >= L)
{
if (type(root) and isContinue(rightL(root), i))
{
right(root) = i, rightL(root) = left(curr);
child[root].push_back(curr);
curr = root;
st.pop();
}
else if (isContinue(left(root), i))
{
dct[++cnt] = DCT(left(root), i, 1,left(curr));
child[cnt].push_back(root), child[cnt].push_back(curr);
curr = cnt;
st.pop();
}
else
{
child[++cnt].push_back(curr);
do child[cnt].push_back(root), st.pop();
while (!isContinue(left(root), i));
dct[cnt] = DCT(left(root), i);
child[cnt].push_back(root);
curr = cnt;
st.pop();
}
}
st.push(curr);
Seg.add(1, 1, i, -1);
}
dctRoot = root;
} ll ans; inline void dfs(const int curr)
{
ll siz = child[curr].size();
if (type(curr))ans += siz * (siz - 1) / 2;
else ans++;
for (const auto nxt : child[curr])dfs(nxt);
} inline void solve()
{
cin >> n;
forn(i, 1, n)
{
int x, y;
cin >> x >> y;
idx[x] = y;
}
ST.build();
build();
dfs(dctRoot);
cout << ans;
} signed int main()
{
// MyFile
Spider
//------------------------------------------------------
// clock_t start = clock();
int test = 1;
// read(test);
// cin >> test;
forn(i, 1, test)solve();
// while (cin >> n, n)solve();
// while (cin >> test)solve();
// clock_t end = clock();
// cerr << "time = " << double(end - start) / CLOCKS_PER_SEC << "s" << endl;
}
\[时间复杂度为 \ O(n\log{n})
\]

CF526F Pudding Monsters 题解的更多相关文章

  1. CF526F Pudding Monsters

    CF526F Pudding Monsters 题目大意:给出一个\(n* n\)的棋盘,其中有\(n\)个格子包含棋子. 每行每列恰有一个棋子. 求\(k*k\)的恰好包含\(k\)枚棋子的子矩形个 ...

  2. 奇袭 CodeForces 526F Pudding Monsters 题解

    考场上没有认真审题,没有看到该题目的特殊之处: 保证每一行和每一列都恰有一只军队,即每一个Xi和每一个Yi都是不一样 的. 于是无论如何也想不到复杂度小于$O(n^3)$的算法, 只好打一个二维前缀和 ...

  3. 「CF526F」 Pudding Monsters

    CF526F Pudding Monsters 传送门 模型转换:对于一个 \(n\times n\) 的棋盘,若每行每列仅有一个棋子,令 \(a_x=y\),则 \(a\) 为一个排列. 转换成排列 ...

  4. 【CF526F】Pudding Monsters cdq分治

    [CF526F]Pudding Monsters 题意:给你一个排列$p_i$,问你有对少个区间的值域段是连续的. $n\le 3\times 10^5$ 题解:bzoj3745 Norma 的弱化版 ...

  5. [Codeforces526F]Pudding Monsters 分治

    F. Pudding Monsters time limit per test 2 seconds memory limit per test 256 megabytes In this proble ...

  6. Codeforces 526F Pudding Monsters - CDQ分治 - 桶排序

    In this problem you will meet the simplified model of game Pudding Monsters. An important process in ...

  7. CodeForces526F:Pudding Monsters (分治)

    In this problem you will meet the simplified model of game Pudding Monsters. An important process in ...

  8. 【CF526F】Pudding Monsters

    题意: 给你一个排列pi,问你有对少个区间的值域段是连续的. n≤3e5 题解: bzoj3745

  9. Codeforces 436D Pudding Monsters

    题意简述 开始有无限长的一段格子,有n个格子种有布丁怪兽,一开始连续的布丁怪兽算一个布丁怪兽. 每回合你可以将一个布丁怪兽向左或右移动,他会在碰到第一个布丁怪兽时停下,并与其合并. 有m个特殊格子,询 ...

  10. [CF436D]Pudding Monsters

    题目大意:有一个长度为$2\times 10^5$的板,有$n(n\leqslant 10^5)$个格子$a_1,\dots,a_n$有布丁怪兽,一开始连续的怪兽算一个怪兽,有$m(m\leqslan ...

随机推荐

  1. 领域驱动设计(DDD)实践之路(二):事件驱动与CQRS

    本文首发于 vivo互联网技术 微信公众号 链接: https://mp.weixin.qq.com/s/Z3uJhxJGDif3qN5OlE_woA作者:wenbo zhang [领域驱动设计实践之 ...

  2. 传统能源转型:数字孪生智慧火电厂 3D 可视化

    前言 火力发电厂满足了全世界将近五成的电力需求,在我国超过半数以上的电力来源于火力发电.随着双碳政策的推行,在国家清洁能源消纳和环保的需求下,对火电厂在深度调峰.超低排放.灵活运行等方面提出了更高要求 ...

  3. keepalived如何手动切换主备

    概述 主备部署中使用keepalived可以很方便的实现,安装维护简单,功能稳定. 最近在使用过程中有小的发现,记录一下. 环境 CentOS Linux release 7.9.2009 (Core ...

  4. docker 资源限制之 cgroup

    1. Liunx cgroup 使用 namespace 隔离运行环境,使得进程像在独立环境中运行一样.然而,仅有隔离环境还不够,还得限制被 namespace 隔离的资源.否则,namespace ...

  5. 用线性二次模型建模大型数据中心,基于 MPC 进行冷却控制

    目录 一个总述 reviews 0 abstract 1 intro 2 related work 3 DC cooling(问题定义) 4 MPC(method) 4.1 Model structu ...

  6. linux 查看服务器cpu 与内存配置

    转载请注明出处: 1. 通过 lscpu 命令查看 服务器的cpu 配置 显示格式: Architecture: #架构 CPU(s): #逻辑cpu颗数 Thread(s) per core: #每 ...

  7. 09-Verilog-并发线程

    Verilog-线程 并发线程 verilog和C语言有一个最大的不同就是并发性 并发--同时进行的过程 module tb_test; int a; initial begin a = 1; a = ...

  8. SQLServer解决deadlock问题的一个场景

    SQLServer解决deadlock问题的一个场景 背景 公司产品出现过很多次dead lock 跟研发讨论了很久, 都没有具体的解决思路 但是这边知道了一个SQLServer数据库上面计划100% ...

  9. [转帖]PD Control 使用说明

    https://docs.pingcap.com/zh/tidb/stable/pd-control PD Control 是 PD 的命令行工具,用于获取集群状态信息和调整集群. 安装方式   注意 ...

  10. [转帖]深入理解mysql-第五章 InnoDB记录存储结构-页结构

    前言: 页是InnoDB管理存储空间的基本单位,上一章我们主要分析了页中的主要的构成行的存储结构-行格式,其中简单提了一下页的概念.这章我们详细讲解一下页的存储结构. 一.数据页结构 前边我们简单提了 ...