题目链接:CF 或者 洛谷

简单来说就是求 \([l,r]\) 这些点都存在的情况下,连通块的数量,看到七秒时限,而且每个点相连的边数很少,可以想到离线下来使用莫队类的算法解决

连通块问题,一般可以考虑使用并查集解决。对于并查集来说,它的增加是非常简单的,但删除是困难的,可持久化并查集时空常数都较大,我们可以考虑一类比较轻松的并查集就是:可撤销并查集。这里可以参考我的这篇 题解 的回滚思想,就知道为啥用可撤销并查集了。所以自然而然地想到了回滚莫队,那么难度就在代码上了。

对于常规的回滚莫队而言,有 \(l\) 与 \(r\) 同块内暴力,不同块采用回滚策略。所以我们需要准备两份并查集,分别维护暴力版本和回滚版本。我们可以考虑把两个并查集的函数合在一起写的,传个参数即可

参考这种并查集我的写法
constexpr int N = 1e5 + 10;
int fa[N], siz[N];
//x,y,fa[x],siz[y]
stack<tuple<int, int, int, int>> st;
int tmpFa[N]; //暴力用的并查集
inline int find(const int x, int* f, const bool isT = false) //是否是暴力版本并查集
{
if (isT)return f[x] == x ? x : f[x] = find(f[x], f, true);
return f[x] == x ? x : find(f[x], f);
} inline bool merge(int x, int y, int* f, const bool isT = false, const bool isNeed = false) //是否需要回顾记录,是否是暴力版本并查集
{
x = find(x, f, isT), y = find(y, f, isT);
if (x == y)return false;
if (isT)
{
f[x] = y;
return true;
}
if (siz[x] > siz[y])swap(x, y);
if (isNeed)st.emplace(x, y, f[x], siz[y]);
f[x] = y, siz[y] += siz[x];
return true;
}

剩下的就简单了,我们维护只加不减并查集,对于 \(r\) 来说放在当前查询块的最右边的前一位代表没有任何后面的点,对于 \(l\) 来说,放在右侧,用于增加块内的点,然后增加以后利用可撤销并查集撤销回去就行。这个过程和 \(l\) 自己的临时加边是互逆的。初识的,我们将每个点当做一个连通块,每 merge 成功一次,我们 \(-1\)。

细节

对于增加的过程在原有的需要判断当前点的邻边点 \(v_{curr}\) 是否有 $ L \le v_{curr} \le R$ 前提下,还得注意一个易错点。如果当前的 \(v_{curr} \le R_{查询块}\),即所连的点在 \(l\) 需要回滚的块中,这时候 \(r\) 并不能和它 merge,否则在 \(l\) 增加到了这个点时,由于已经 merge 过了,并不会在栈中存储回滚信息,导致无法回滚回到 \([R_{查询块},r]\) 这个初始回滚状态,在后续这个不存在的 \(v_{curr}\) 会继续造成贡献。所以在 \(r\) 的 add 过程中,这类点需要先跳过,在 \(l\) 增加时会自然加上再自然地回滚。

最终复杂度显然为:

\[ O(kq\sqrt{n}\log{\sqrt{n}})
\]
参考代码
#include <bits/stdc++.h>

//#pragma GCC optimize("Ofast,unroll-loops")

// #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);
} char ch; template <typename T>
void read(T& x)
{
x = 0;
T sign = 1;
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>
void write(T x)
{
if (x < 0)x = -x, putchar('-');
if (x > 9)write(x / 10);
putchar(x % 10 ^ 48);
} 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 = 1e5 + 10;
int fa[N], siz[N];
//x,y,fa[x],siz[y]
stack<tuple<int, int, int, int>> st;
int tmpFa[N]; //暴力用的并查集
inline int find(const int x, int* f, const bool isT = false) //是否是暴力版本并查集
{
if (isT)return f[x] == x ? x : f[x] = find(f[x], f, true);
return f[x] == x ? x : find(f[x], f);
} inline bool merge(int x, int y, int* f, const bool isT = false, const bool isNeed = false) //是否需要回顾记录,是否是暴力版本并查集
{
x = find(x, f, isT), y = find(y, f, isT);
if (x == y)return false;
if (isT)
{
f[x] = y;
return true;
}
if (siz[x] > siz[y])swap(x, y);
if (isNeed)st.emplace(x, y, f[x], siz[y]);
f[x] = y, siz[y] += siz[x];
return true;
} int pos[N]; struct Mo
{
int l, r, id;
//只加不减回滚莫队
bool operator<(const Mo& other) const
{
return pos[l] != pos[other.l] ? pos[l] < pos[other.l] : r < other.r;
}
} node[N]; inline void del()
{
while (!st.empty())
{
auto [x,y,faX,sizY] = st.top();
st.pop();
fa[x] = faX, siz[y] = sizY;
}
} int ans[N];
int n, m, q;
int curr, last;
int e[N];
vector<int> child[N]; inline void solve()
{
cin >> n >> m >> m;
forn(i, 1, n)fa[i] = tmpFa[i] = i, siz[i] = 1;
forn(i, 1, m)
{
int u, v;
cin >> u >> v;
if (u > v)swap(u, v);
child[u].push_back(v);
child[v].push_back(u);
}
cin >> q;
forn(i, 1, q)cin >> node[i].l >> node[i].r, node[i].id = i;
const int blockSize = ceil(sqrt(n));
const int cnt = (n + blockSize - 1) / blockSize;
forn(i, 1, n)pos[i] = (i - 1) / blockSize + 1;
forn(i, 1, cnt)e[i] = i * blockSize;
e[cnt] = n;
sortArr(node, q);
int l = 1, r = 0;
forn(i, 1, q)
{
const auto [L,R,id] = node[i];
if (pos[L] == pos[R])
{
int tmp = R - L + 1;
forn(idx, L, R)
{
for (const auto j : child[idx])
{
if (j < L or R < j)continue;;
tmp -= merge(idx, j, tmpFa, true);
}
}
ans[id] = tmp;
forn(idx, L, R)tmpFa[idx] = idx;
continue;
}
if (pos[L] != last)
{
forn(idx, 1, n)fa[idx] = idx, siz[idx] = 1;
l = e[pos[L]] + 1, r = l - 1;
curr = 0;
last = pos[L];
}
while (r < R)
{
++r, ++curr;
for (const int pr : child[r])
{
if (pr < L or R < pr or pr < l)continue;;
curr -= merge(r, pr, fa);
}
}
int tmp = curr;
int tmpL = l;
while (tmpL > L)
{
tmpL--, tmp++;
for (const auto pl : child[tmpL])
{
if (pl < L or R < pl)continue;
tmp -= merge(tmpL, pl, fa, false, true);
}
}
ans[id] = tmp;
del();
}
forn(i, 1, q)cout << ans[i] << endl;
} signed int main()
{
// MyFile
Spider
//------------------------------------------------------
int test = 1;
// read(test);
// cin >> test;
forn(i, 1, test)solve();
// while (cin >> n, n)solve();
// while (cin >> test)solve();
}

CF763E Timofey and our friends animals题解的更多相关文章

  1. CF763E Timofey and our friends animals

    题目戳这里. 首先题解给的是并查集的做法.这个做法很好想,但是很难码.用线段树数来维护并查集,暴力合并. 这里推荐另一个做法,可以无视\(K\)的限制.我们给每条边加个边权,这个边权为这条边左端点的值 ...

  2. CF764B Timofey and cubes 题解

    Content 有一个序列 \(a_1,a_2,a_3,...,a_n\),对于 \(i\in[1,n]\),只要 \(i\leqslant n-i+1\),就把闭区间 \([i,n-i+1]\) 内 ...

  3. Codeforces Round #371 (Div. 1) D. Animals and Puzzle 二维倍增

    D. Animals and Puzzle 题目连接: http://codeforces.com/contest/713/problem/D Description Owl Sonya gave a ...

  4. 【CodeForces】713 D. Animals and Puzzle 动态规划+二维ST表

    [题目]D. Animals and Puzzle [题意]给定n*m的01矩阵,Q次询问某个子矩阵内的最大正方形全1子矩阵边长.n,m<=1000,Q<=10^6. [算法]动态规划DP ...

  5. The Child and Zoo 题解

    题目描述 Of course our child likes walking in a zoo. The zoo has n areas, that are numbered from 1 to n. ...

  6. Codeforces_764_C. Timofey and a tree_(并查集)(dfs)

    C. Timofey and a tree time limit per test 2 seconds memory limit per test 256 megabytes input standa ...

  7. CF 1131A,1131B,1131C,1131D,1131F(Round541 A,B,C,D,F)题解

    A. Sea Battle time limit per test 1 second memory limit per test 256 megabytes input standard input ...

  8. 【codeforces 764B】Timofey and cubes

    time limit per test1 second memory limit per test256 megabytes inputstandard input outputstandard ou ...

  9. 【codeforces 764C】Timofey and a tree

    time limit per test2 seconds memory limit per test256 megabytes inputstandard input outputstandard o ...

  10. Codeforces 764C Timofey and a tree

    Each New Year Timofey and his friends cut down a tree of n vertices and bring it home. After that th ...

随机推荐

  1. 【每日一题】15.Xorto (前缀和枚举)

    补题链接:Here 题意:选取任意不重叠的两个区间,使异或结果为 \(0\) 样例:\(1,2,3,4,5,5\) 在样例中我们可以选取 \(1,2,3\) 和 \(5,5\) 就是满足题意 思路:相 ...

  2. 为什么 Serverless 能提升资源利用率?

    木吴|阿里云智能高级技术专家 业务的负载往往不是一成不变的,而是随着时间呈现一定的上下波动.传统的应用构建方式一般是备足充分的资源以保障业务可用性,造成资源利用率不高的现象.随着容器技术的普及,应用可 ...

  3. PHPCMS V9安装出现DNS解析失败的解决方法-不支持采集和保存远程图片

    目前因为phpcms官网停止解析后,很多人安装phpcms v9出现如下错误:     这是因为检测dns解析的域名是phpcms官网的域名,官网域名停止解析后肯定检测失败.解决方法如下:   打开/ ...

  4. 百度网盘(百度云)SVIP超级会员共享账号每日更新(2023.12.18)

    一.百度网盘SVIP超级会员共享账号 可能很多人不懂这个共享账号是什么意思,小编在这里给大家做一下解答. 我们多知道百度网盘很大的用处就是类似U盘,不同的人把文件上传到百度网盘,别人可以直接下载,避免 ...

  5. 百度网盘(百度云)SVIP超级会员共享账号每日更新(2023.11.17)

    一.百度网盘SVIP超级会员共享账号 可能很多人不懂这个共享账号是什么意思,小编在这里给大家做一下解答. 我们多知道百度网盘很大的用处就是类似U盘,不同的人把文件上传到百度网盘,别人可以直接下载,避免 ...

  6. [转帖]Grafana+influxdb+ntopng简易网络流量分析展示系统

    Grafana逼格高,所以用它展示ntopng的数据 >_< 一,ntopng 根据官网资料 https://www.ntop.org/ntop/ntopng-influxdb-and-g ...

  7. [粘贴]github-redis-rdb-cli

    redis-rdb-cli A tool that can parse, filter, split, merge rdb and analyze memory usage offline. It c ...

  8. Nginx双层域名时 iframe嵌入/跳转页面的处理过程

    Nginx双层域名时 iframe嵌入/跳转页面的处理过程 背景 两年前在上一家公司内遇到一个Nginx的问题 当时的场景是 双层nginx代理时(一层域名侧, 一层拆分微服务的网关层) 程序里面会打 ...

  9. [转帖]iptables的四表五链与NAT工作原理

    本文主要介绍了iptables的基本工作原理和四表五链等基本概念以及NAT的工作原理. 1.iptables简介 我们先来看一下netfilter官网对iptables的描述: iptables is ...

  10. [转帖]SPEC2006

    安装步骤 # Ubuntu16.04 # 注意安装gFortran . ./install.sh . ./shrc 一般情况下经过以上步骤即可安装完毕,进行使用,注意需要执行shrc设置完环境变量以后 ...