The Hangzhou Normal U Qualification Trials for Zhejiang Provincial Collegiate Programming Contest 2020 Editorial
Contents
- Our Sponsor
- Thanks
- Talks
- Materials
- Solutions
- A. A simple problem
- B. Hsueh- play balls
- C. Hoogle Machine Translation
- D. Dup4 and pebble pile
- E. The King of Sum Xor
- F. Hsueh- Love Matrix
- G. LTS owns large quantities of apples
- H. Hsueh- and keyboard
- I. LTS and rectangular area union
- J. Hsueh- owns large quantities of apples
- K. LTS buy wine
- L. Line problem
- M. Rikka with Random Graph
Our Sponsor
- 本场比赛由 DMI(数字媒体与人机交互)中心赞助。
- DMI 中心主要研究方向:
- 智能视频编码:借助于深度学习技术,将其应用于视频编码中,提高压缩效率。
- 图像质量增强:借助于机器学习,提高受损图像主客观质量或分辨率。
- 视频编码算法优化:设计快速算法,在保证视频编码效率的同时提高编码速度。
- DMI 中心拥有:
- 100 平米的实验室。
- 20 台可进行深度学习的机器,2080TI 是标配。
- DMI 中心联系人:
- 丁丹丹老师,浙江大学博士学位。
- QQ: 187113186
- Email: DandanDing@hznu.edu.cn
Thanks
- 感谢所有验题人给我们提出的宝贵的意见。
Talks
- 这个随机会不会被人打死。
- 果然「好题目是改出来的」,当你感觉良好的时候,总有验题人来给你当头棒喝,这里的「好」指的是题面阐述清楚,数据健壮,而非「好题」(逃。
Materials
account
Handle | Password | Name |
---|---|---|
team2950361 | 16PGIH | HZNU_kindergarten-C5-包敏、孙周毅、魏炜 |
team2950362 | 4R3PJ9 | Fingertip melody-D3-谢作杰、卢霖统、杨昌林 |
team2950363 | 5I1SMD | HZNU_Tourists-A3-张凯莉、邱龙风、张传 |
team2950364 | 53BJ3G | Schroedinger's Pig-C1-朱湖健、陆家辉、梁文博 |
team2950365 | 6AERHL | Cabbage-B5-施骏炜、陈思欣、毛昱滢 |
team2950366 | GVKNB4 | Three Fruits-D5-温铭浩、魏依洋、李浩然 |
team2950367 | XGFX11 | pupil NB-C3-俞佳权、王凌言、钟恺 |
team2950368 | UJVQEJ | washing washing sleeping -D2-何陈聪、付俊、李骋 |
team2950369 | UZHKAE | Kitten Marine Corp-A4-刘恒羽、叶初航、周婉婧 |
team29503610 | IDM6ND | yingyingying-B4-邵铁夫、温声荣、苏桐渤 |
team29503611 | ZEXXUY | O(n!) -> O(1)-C4-张枨、陈柯涛、吴陈宇 |
team29503612 | YQ2QQH | acwork-A2-全振宇、宋博帆、王艺蓉 |
team29503613 | GI9PXJ | NULL-B1-林峰、梁涵杰、徐光泽 |
team29503614 | C82J2C | Turing-D4-陈雨欣、周珈伊、徐豪杰 |
team29503615 | 2S3CJB | play snake-B2-李彦庆、杨云龙、林泓佺 |
team29503616 | 2ZFGJA | Fried tomatoes-C2-毛潜飞、方一昊、叶俊 |
team29503617 | H8ZIP2 | dancing lightning-B3-徐浩然、梁雨欣、杨帆 |
team29503618 | 2R6WYC | non-prepared guys-A5-蔡林达、高笑芸、刘兴松 |
Solutions
A. A simple problem
题意
给出
思路
看数据范围经典状压
Code
#include <bits/stdc++.h>
using namespace std;
#define dbg(x...) \
do { \
cout << #x << " -> "; \
err(x); \
} while (0)
void err() {
cout << endl;
}
template <class T, class... Ts>
void err(const T &arg, const Ts &...args) {
cout << arg << ' ';
err(args...);
}
#define endl "\n"
#define all(A) A.begin(), A.end()
using ll = long long;
using db = double;
using pII = pair<int, int>;
const int INF = 0x3f3f3f3f;
const ll INFLL = 0x3f3f3f3f3f3f3f3f;
const int N = 16, M = 100;
int n, m;
ll f[1 << N][M];
void RUN() {
cin >> n >> m;
++n;
f[0][0] = 1;
int limit = 1 << n;
for (int S = 0; S < limit; ++S) {
for (int i = 0; i < n; ++i) {
if (S & (1 << i))
continue;
if (S == 0 && i == 0)
continue;
int nxt = S | (1 << i);
for (int j = 0; j < m; ++j) {
if (i >= 10) {
f[nxt][(j * 100 + i) % m] += f[S][j];
} else {
f[nxt][(j * 10 + i) % m] += f[S][j];
}
}
}
}
cout << f[limit - 1][0] << endl;
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr), cout.tie(nullptr);
cout << fixed << setprecision(20);
RUN();
return 0;
}
B. Hsueh- play balls
题意
有
思路
答案是:
考虑此处概率就是
总方案数显然是
合法的方案数是什么,就是取球过程中白球个数和黑球个数至少有一次相等的方案,考虑反面,就是
我们不妨令
首先我们需要两条预备知识:
- 从
到 的非降路径条数: - 从
到 的非降路径条数:
那么我们转化一下,看成坐标系中,从
- 总的情况数为
先向上走到 ,那么到终点 一定会经过 这条直线,这种非法情况为 到 的非降路径条数 - 从
向右走到 ,这时候有两种情况,一种情况是合法的,另一种情况还是会经过 ,这时候记该路线一次经过 的点为 ,将 到点 间的路径关于 对称,可以得到与情况 的一一映射,即不合法情况的路径条数为 - 所以总的满足条件的路径条数为
。 - 也可以直接考虑强制第一步往右走,那么总的方案数为
,再考虑上述的第三种情况的不合法方案数,那么不合法的有 ,所以合法方案数为
Code
#include <bits/stdc++.h>
using namespace std;
#define endl "\n"
#define fi first
#define se second
#define SZ(x) ((int)(x).size())
#define mkp make_pair
#define all(x) (x).begin(), (x).end()
using db = double;
using ll = long long;
using ull = unsigned long long;
using pII = pair<int, int>;
using pLL = pair<ll, ll>;
constexpr int mod = 998244353;
template <class T1, class T2>
inline void chadd(T1 &x, T2 y, int Mod = mod) {
x += y;
while (x >= Mod) x -= Mod;
while (x < 0) x += Mod;
}
template <class T1, class T2>
inline void chmax(T1 &x, T2 y) {
if (x < y)
x = y;
}
template <class T1, class T2>
inline void chmin(T1 &x, T2 y) {
if (x > y)
x = y;
}
inline int nextInt() {
int x;
cin >> x;
return x;
}
void rd() {}
template <class T, class... Ts>
void rd(T &arg, Ts &...args) {
cin >> arg;
rd(args...);
}
#define dbg(x...) \
do { \
cout << "\033[32;1m" << #x << " -> "; \
err(x); \
} while (0)
void err() {
cout << "\033[39;0m" << endl;
}
template <class T, class... Ts>
void err(const T &arg, const Ts &...args) {
cout << arg << ' ';
err(args...);
}
template <template <typename...> class T, typename t, typename... A>
void err(const T<t> &arg, const A &...args) {
for (auto &v : arg) cout << v << ' ';
err(args...);
}
void ptt() {
cout << endl;
}
template <class T, class... Ts>
void ptt(const T &arg, const Ts &...args) {
cout << ' ' << arg;
ptt(args...);
}
template <class T, class... Ts>
void pt(const T &arg, const Ts &...args) {
cout << arg;
ptt(args...);
}
void pt() {}
template <template <typename...> class T, typename t, typename... A>
void pt(const T<t> &arg, const A &...args) {
for (int i = 0, sze = arg.size(); i < sze; ++i) cout << arg[i] << " \n"[i == sze - 1];
pt(args...);
}
inline ll qpow(ll base, ll n) {
assert(n >= 0);
ll res = 1;
while (n) {
if (n & 1)
res = res * base % mod;
base = base * base % mod;
n >>= 1;
}
return res;
}
// head
constexpr int N = 2e6 + 10;
int n, m, fac[N], inv[N];
ll C(int n, int m) {
if (n < m)
return 0;
return 1ll * fac[n] * inv[n - m] % mod * inv[m] % mod;
}
void run() {
rd(n, m);
if (n == m)
return pt(1);
if (n < m)
swap(n, m);
ll tot = C(n + m, n);
ll cur = (C(n + m - 1, n - 1) - C(n + m - 1, m - 1) + mod) % mod;
ll numerator = (tot - cur + mod) % mod;
ll denominator = tot;
// dbg(numerator, denominator);
ll res = numerator * qpow(denominator, mod - 2) % mod;
pt(res);
}
int main() {
fac[0] = 1;
for (int i = 1; i < N; ++i) fac[i] = 1ll * fac[i - 1] * i % mod;
inv[N - 1] = qpow(fac[N - 1], mod - 2);
for (int i = N - 1; i >= 1; --i) inv[i - 1] = 1ll * inv[i] * i % mod;
ios::sync_with_stdio(false);
cin.tie(nullptr);
cout.tie(nullptr);
cout << fixed << setprecision(20);
int _T = nextInt();
while (_T--) run();
// for (int kase = 1; kase <= _T; ++kase) {
// cout << "Case #" << kase << ": ";
// run();
// }
// while (cin >> n) run();
// run();
return 0;
}
C. Hoogle Machine Translation
题意
有一个翻译机器,每次能翻译若干个单词,但是返回的释义的顺序是混乱的,现在要求在
思路
做法一
显然,需要用
那么我们对每个单词从
最终,每个释义可以根据在询问中的出现情况,一一对应到一个单词。
Code
#include <bits/stdc++.h>
using namespace std;
#define endl "\n"
#define fi first
#define se second
#define SZ(x) ((int)(x).size())
#define mkp make_pair
#define all(x) (x).begin(), (x).end()
using db = double;
using ll = long long;
using ull = unsigned long long;
using pII = pair<int, int>;
using pLL = pair<ll, ll>;
constexpr int mod = 1e9 + 7;
template <class T1, class T2>
inline void chadd(T1 &x, T2 y, int Mod = mod) {
x += y;
while (x >= Mod) x -= Mod;
while (x < 0) x += Mod;
}
template <class T1, class T2>
inline void chmax(T1 &x, T2 y) {
if (x < y)
x = y;
}
template <class T1, class T2>
inline void chmin(T1 &x, T2 y) {
if (x > y)
x = y;
}
inline int nextInt() {
int x;
cin >> x;
return x;
}
void rd() {}
template <class T, class... Ts>
void rd(T &arg, Ts &...args) {
cin >> arg;
rd(args...);
}
#define dbg(x...) \
do { \
cout << "\033[32;1m" << #x << " -> "; \
err(x); \
} while (0)
void err() {
cout << "\033[39;0m" << endl;
}
template <class T, class... Ts>
void err(const T &arg, const Ts &...args) {
cout << arg << ' ';
err(args...);
}
template <template <typename...> class T, typename t, typename... A>
void err(const T<t> &arg, const A &...args) {
for (auto &v : arg) cout << v << ' ';
err(args...);
}
void ptt() {
cout << endl;
}
template <class T, class... Ts>
void ptt(const T &arg, const Ts &...args) {
cout << ' ' << arg;
ptt(args...);
}
template <class T, class... Ts>
void pt(const T &arg, const Ts &...args) {
cout << arg;
ptt(args...);
}
void pt() {}
template <template <typename...> class T, typename t, typename... A>
void pt(const T<t> &arg, const A &...args) {
for (int i = 0, sze = arg.size(); i < sze; ++i) cout << arg[i] << " \n"[i == sze - 1];
pt(args...);
}
inline ll qpow(ll base, ll n) {
assert(n >= 0);
ll res = 1;
while (n) {
if (n & 1)
res = res * base % mod;
base = base * base % mod;
n >>= 1;
}
return res;
}
// head
constexpr int N = 1e5 + 10;
int n;
map<string, int> mp;
void send(const vector<string> &vec) {
cout << "? " << SZ(vec);
for (auto &it : vec) {
cout << " " << it;
}
cout << endl;
cout.flush();
}
void got(int n, int x) {
string s;
for (int i = 0; i < n; ++i) {
rd(s);
int it = mp[s];
mp[s] = it | (1 << x);
}
}
void run() {
rd(n);
vector<string> vec(n);
for (auto &it : vec) rd(it);
mp.clear();
for (int i = 0; i < 20; ++i) {
vector<string> query;
for (int j = 1; j <= n; ++j) {
if ((j >> i) & 1) {
query.push_back(vec[j - 1]);
}
}
if (SZ(query)) {
send(query);
got(SZ(query), i);
}
}
vector<string> res(n);
for (auto &it : mp) res[it.se - 1] = it.fi;
cout << "!";
for (auto &it : res) cout << " " << it;
cout << endl;
cout.flush();
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
cout.tie(nullptr);
cout << fixed << setprecision(20);
int _T = 1;
while (_T--) run();
// for (int kase = 1; kase <= _T; ++kase) {
// cout << "Case #" << kase << ": ";
// run();
// }
// while (cin >> n) run();
// run();
return 0;
}
做法二
- 第一次询问所有的单词。
- 第二次将第一次询问的单词分成两半,继续询问,这个时候可以根据第一次和第二次询问的结果,将单词分成两半。
- 继续分下去即可。
- 咋一想,询问次数好像是
,但是我们对于每一层,并不需要分开询问,完全可以放在一起询问,因为哪些释义属于那一堆都已经知道了,那完全可以把每一层的询问放在一个询问里。这样询问的次数其实就是层数了,那显然就是 。 - 但是后来一想,第
次询问后,而且要分开询问,才能将第一次询问的所有释义分成两半,第 次询问也要分开两次才能将第 次的释义分成两部分。 - 所以次数好像要乘个
,这样的话, 次询问次数,就有点不够了。
D. Dup4 and pebble pile
题意
有一些鹅卵石,编号为
接下来,每次可以选择两个属于不同堆的鹅卵石
问直到不能再合并为止,最终有多少堆鹅卵石。
思路
做法一
可以对每个大于等于
最终答案是联通块个数。
做法二
直接枚举大于等于
最终答案是联通块个数。
时间复杂度
Code
#include <bits/stdc++.h>
using namespace std;
#define endl "\n"
#define fi first
#define se second
#define SZ(x) ((int)(x).size())
#define mkp make_pair
#define all(x) (x).begin(), (x).end()
using db = double;
using ll = long long;
using ull = unsigned long long;
using pII = pair<int, int>;
using pLL = pair<ll, ll>;
constexpr int mod = 1e9 + 7;
template <class T1, class T2>
inline void chadd(T1 &x, T2 y, int Mod = mod) {
x += y;
while (x >= Mod) x -= Mod;
while (x < 0) x += Mod;
}
template <class T1, class T2>
inline void chmax(T1 &x, T2 y) {
if (x < y)
x = y;
}
template <class T1, class T2>
inline void chmin(T1 &x, T2 y) {
if (x > y)
x = y;
}
inline int nextInt() {
int x;
cin >> x;
return x;
}
void rd() {}
template <class T, class... Ts>
void rd(T &arg, Ts &...args) {
cin >> arg;
rd(args...);
}
#define dbg(x...) \
do { \
cout << "\033[32;1m" << #x << " -> "; \
err(x); \
} while (0)
void err() {
cout << "\033[39;0m" << endl;
}
template <class T, class... Ts>
void err(const T &arg, const Ts &...args) {
cout << arg << ' ';
err(args...);
}
template <template <typename...> class T, typename t, typename... A>
void err(const T<t> &arg, const A &...args) {
for (auto &v : arg) cout << v << ' ';
err(args...);
}
void ptt() {
cout << endl;
}
template <class T, class... Ts>
void ptt(const T &arg, const Ts &...args) {
cout << ' ' << arg;
ptt(args...);
}
template <class T, class... Ts>
void pt(const T &arg, const Ts &...args) {
cout << arg;
ptt(args...);
}
void pt() {}
template <template <typename...> class T, typename t, typename... A>
void pt(const T<t> &arg, const A &...args) {
for (int i = 0, sze = arg.size(); i < sze; ++i) cout << arg[i] << " \n"[i == sze - 1];
pt(args...);
}
inline ll qpow(ll base, ll n) {
assert(n >= 0);
ll res = 1;
while (n) {
if (n & 1)
res = res * base % mod;
base = base * base % mod;
n >>= 1;
}
return res;
}
// head
constexpr int N = 1e5 + 10;
int a, b, p, f[N];
struct UFS {
int fa[N];
void init() {
memset(fa, 0, sizeof fa);
}
int find(int x) {
return fa[x] == 0 ? x : fa[x] = find(fa[x]);
}
void merge(int x, int y) {
int fx = find(x), fy = find(y);
if (fx != fy) {
fa[fx] = fy;
}
}
} ufs;
void run() {
rd(a, b, p);
ufs.init();
memset(f, 0, sizeof f);
for (int i = 2; i <= b; ++i) {
if (f[i])
continue;
int pre = i;
for (int j = i; j <= b; j += i) {
f[j] = 1;
if (i >= p && pre >= a) {
ufs.merge(pre, j);
}
pre = j;
}
}
int res = 0;
memset(f, 0, sizeof f);
for (int i = a; i <= b; ++i) {
if (ufs.find(i) == i)
++res;
}
pt(res);
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
cout.tie(nullptr);
cout << fixed << setprecision(20);
int _T = 1;
while (_T--) run();
// for (int kase = 1; kase <= _T; ++kase) {
// cout << "Case #" << kase << ": ";
// run();
// }
// while (cin >> n) run();
// run();
return 0;
}
E. The King of Sum Xor
题意
长度为
将满足这种情况的数组的最大值定义为
思路
首先考虑 impossible
的情况
为奇数
我们将这三个数按照二进制分解将会得到一个出事情况的
为了满足关系,我们每次执行以下操作
接下来考虑最小的
然后我们就可以解决这个问题
首先二分一个
考虑如何优化这个复杂度
我们可以发现初始状态中
PS: 懒得改 std,就放了个
Code
#include <bits/stdc++.h>
using namespace std;
#define dbg(x...) \
do { \
cout << #x << " -> "; \
err(x); \
} while (0)
void err() {
cout << endl;
}
template <class T, class... Ts>
void err(const T &arg, const Ts &...args) {
cout << arg << ' ';
err(args...);
}
#define endl "\n"
#define all(A) A.begin(), A.end()
using ll = long long;
using db = double;
using pII = pair<int, int>;
const int INF = 0x3f3f3f3f;
const ll INFLL = 0x3f3f3f3f3f3f3f3f;
const int N = 70;
ll s, x;
int b[N];
int high;
// n:limit of high, m limit of low
bool ok(ll n, ll m) {
ll remind = 0;
for (int i = 59; i >= 0; --i) {
remind <<= 1;
remind += b[i];
if (i > high)
continue;
ll dec = (i == high ? n : m);
remind = max(0ll, remind - dec);
if (remind & 1) {
if (!dec)
return false;
remind++;
}
}
return remind == 0;
}
bool check(ll n) {
for (int i = 0; i <= 5; ++i) {
if (n - i >= 0 && ok(n - i, i)) {
return true;
}
}
return false;
}
void RUN() {
cin >> s >> x;
if (s < x || (s - x) % 2 == 1) {
cout << -1 << endl;
return;
}
if (s == x && s == 0) {
cout << 0 << endl;
return;
}
memset(b, 0, sizeof b);
high = 0;
for (int i = 0; i < 60; ++i) {
b[i] = (x >> i & 1) + ((s - x) >> i & 2);
if ((x >> i) & 1) {
high = max(high, i);
}
}
ll l = 1, r = s, res = -1;
while (r - l >= 0) {
ll mid = (l + r) >> 1;
if (check(mid)) {
r = mid - 1;
res = mid;
} else {
l = mid + 1;
}
}
cout << res << endl;
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr), cout.tie(nullptr);
cout << fixed << setprecision(20);
int _T;
cin >> _T;
while (_T--) {
RUN();
}
return 0;
}
F. Hsueh- Love Matrix
题意
给出一个
思路
求第
那么我们二分答案
那么这个计数,其实等价于求:
那么这个东西用数论分块,就可以在
Code
#include <bits/stdc++.h>
using namespace std;
#define endl "\n"
#define fi first
#define se second
#define SZ(x) ((int)(x).size())
#define mkp make_pair
#define all(x) (x).begin(), (x).end()
using db = double;
using ll = long long;
using ull = unsigned long long;
using pII = pair<int, int>;
using pLL = pair<ll, ll>;
constexpr int mod = 1e9 + 7;
template <class T1, class T2>
inline void chadd(T1 &x, T2 y, int Mod = mod) {
x += y;
while (x >= Mod) x -= Mod;
while (x < 0) x += Mod;
}
template <class T1, class T2>
inline void chmax(T1 &x, T2 y) {
if (x < y)
x = y;
}
template <class T1, class T2>
inline void chmin(T1 &x, T2 y) {
if (x > y)
x = y;
}
inline int nextInt() {
int x;
cin >> x;
return x;
}
void rd() {}
template <class T, class... Ts>
void rd(T &arg, Ts &...args) {
cin >> arg;
rd(args...);
}
#define dbg(x...) \
do { \
cout << "\033[32;1m" << #x << " -> "; \
err(x); \
} while (0)
void err() {
cout << "\033[39;0m" << endl;
}
template <class T, class... Ts>
void err(const T &arg, const Ts &...args) {
cout << arg << ' ';
err(args...);
}
template <template <typename...> class T, typename t, typename... A>
void err(const T<t> &arg, const A &...args) {
for (auto &v : arg) cout << v << ' ';
err(args...);
}
void ptt() {
cout << endl;
}
template <class T, class... Ts>
void ptt(const T &arg, const Ts &...args) {
cout << ' ' << arg;
ptt(args...);
}
template <class T, class... Ts>
void pt(const T &arg, const Ts &...args) {
cout << arg;
ptt(args...);
}
void pt() {}
template <template <typename...> class T, typename t, typename... A>
void pt(const T<t> &arg, const A &...args) {
for (int i = 0, sze = arg.size(); i < sze; ++i) cout << arg[i] << " \n"[i == sze - 1];
pt(args...);
}
inline ll qpow(ll base, ll n) {
assert(n >= 0);
ll res = 1;
while (n) {
if (n & 1)
res = res * base % mod;
base = base * base % mod;
n >>= 1;
}
return res;
}
// head
ll n, m, k;
bool ok(ll x) {
ll tot = 0;
for (int i = 1, j; i <= min(n, x); i = j + 1) {
j = min(n, x / (x / i));
tot += 1ll * (j - i + 1) * min(m, x / i);
}
return tot >= k;
}
void run() {
rd(n, m, k);
k = n * m - k + 1;
if (n > m)
swap(n, m);
ll l = 1, r = min(n * m, 10000000000ll), res = r;
// ll l = 1, r = n * m, res = r;
while (r - l >= 0) {
ll mid = (l + r) >> 1;
if (ok(mid)) {
r = mid - 1;
res = mid;
} else {
l = mid + 1;
}
}
// pt(res);
if (res > 9999999999ll)
pt("Oops");
else
pt(res);
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
cout.tie(nullptr);
cout << fixed << setprecision(20);
int _T = nextInt();
while (_T--) run();
// for (int kase = 1; kase <= _T; ++kase) {
// cout << "Case #" << kase << ": ";
// run();
// }
// while (cin >> n) run();
// run();
return 0;
}
G. LTS owns large quantities of apples
题意
有
- 第一个孩子拿到了
个苹果,他吃掉了一下,发现剩下的苹果能恰好分成 堆,他拿走了一堆,将剩下的 堆给第二个孩子。 - 第二个孩子拿到了
个苹果,吃掉了一个,发现剩下的苹果能恰好分成 堆,他拿走了一堆,将剩下的 堆给第三个孩子。 - 第
个孩子拿到了第 个孩子给他的苹果,吃掉了一个,发现剩下的苹果能恰好分成 堆,拿走了一堆,将剩下的 堆给第 个孩子。 - 最后一个孩子拿到了倒数第二个孩子给他的苹果,吃掉了一个,发现剩下的苹果能恰好分成
堆,拿走了一堆,然后留下了 堆,就离开了。
现在给出
思路
一个合法的解是:
但是
我们考虑
可以推断出:
我们再考虑倒着推:
我们发现
它在这一步操作中并没有变化,但是因为它是负数,所以
我们再考虑
又因为
Code
#include <bits/stdc++.h>
using namespace std;
#define endl "\n"
#define fi first
#define se second
#define SZ(x) ((int)(x).size())
#define mkp make_pair
#define all(x) (x).begin(), (x).end()
using db = double;
using ll = long long;
using ull = unsigned long long;
using pII = pair<int, int>;
using pLL = pair<ll, ll>;
constexpr int mod = 1e9 + 7;
template <class T1, class T2>
inline void chadd(T1 &x, T2 y, int Mod = mod) {
x += y;
while (x >= Mod) x -= Mod;
while (x < 0) x += Mod;
}
template <class T1, class T2>
inline void chmax(T1 &x, T2 y) {
if (x < y)
x = y;
}
template <class T1, class T2>
inline void chmin(T1 &x, T2 y) {
if (x > y)
x = y;
}
inline int nextInt() {
int x;
cin >> x;
return x;
}
void rd() {}
template <class T, class... Ts>
void rd(T &arg, Ts &...args) {
cin >> arg;
rd(args...);
}
#define dbg(x...) \
do { \
cout << "\033[32;1m" << #x << " -> "; \
err(x); \
} while (0)
void err() {
cout << "\033[39;0m" << endl;
}
template <class T, class... Ts>
void err(const T &arg, const Ts &...args) {
cout << arg << ' ';
err(args...);
}
template <template <typename...> class T, typename t, typename... A>
void err(const T<t> &arg, const A &...args) {
for (auto &v : arg) cout << v << ' ';
err(args...);
}
void ptt() {
cout << endl;
}
template <class T, class... Ts>
void ptt(const T &arg, const Ts &...args) {
cout << ' ' << arg;
ptt(args...);
}
template <class T, class... Ts>
void pt(const T &arg, const Ts &...args) {
cout << arg;
ptt(args...);
}
void pt() {}
template <template <typename...> class T, typename t, typename... A>
void pt(const T<t> &arg, const A &...args) {
for (int i = 0, sze = arg.size(); i < sze; ++i) cout << arg[i] << " \n"[i == sze - 1];
pt(args...);
}
inline ll qpow(ll base, ll n) {
assert(n >= 0);
ll res = 1;
while (n) {
if (n & 1)
res = res * base % mod;
base = base * base % mod;
n >>= 1;
}
return res;
}
// head
int m, x;
ll POW(ll base, int n) {
ll res = 1;
while (n) {
if (n & 1)
res = res * base;
base = base * base;
n >>= 1;
}
return res;
}
void run() {
rd(m, x);
if (m == 1)
pt(x + 1);
else if (x == 2) {
ll res = 1;
for (int i = 1; i <= m; ++i) {
res = res * 2 + 1;
}
pt(res);
} else {
ll res = POW(x, m) - (x - 1);
pt(res);
}
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
cout.tie(nullptr);
cout << fixed << setprecision(20);
int _T = 1;
while (_T--) run();
// for (int kase = 1; kase <= _T; ++kase) {
// cout << "Case #" << kase << ": ";
// run();
// }
// while (cin >> n) run();
// run();
return 0;
}
H. Hsueh- and keyboard
题意
文本框里刚开始有一个长度为
你可以通过以下操作达成目标:
- 按下键盘一次,输入一个字符。
- 按下键盘两次(Ctrl + A), 选中文本框中的所有字符。
- 按下键盘两次(Ctrl + C), 复制选中的字符到剪贴板。
- 按下键盘两次(Ctrl + V), 将剪贴板中的字符粘贴到文本框。
- 按下键盘一次(Backspace), 删除文本框中的最后一个字符或者删除选中的字符,如果在这之前你按下了(Ctrl + A)选中了一些字符的话。
此处的操作和传统认知有所不同的是,如果你先按下了(Ctrl + A)选中了所有字符,再输入一个字符,或者粘贴剪贴板中的文字到文本框,那么它不会产生替换选中文字的效果,而是会直接当前字符串追加在后面。 或者你可以认为(Ctrl + A)只会对复制操作有效。
思路
从
- 显然,选中、复制、粘贴肯定是连着用,不会出现先复制,然后粘贴,然后进行一些其他的操作,然后再复制。但是粘贴可能会粘贴多次。
- 选中和删除也是连着用。
- 那么直接建图连边即可,比如:
- 第一种操作:就是
向 连边。 - 选中、复制、粘贴,就是
到 连边。 - 删除,就是
向 连边,或者 向 连边。 - 注意加上边权。
Code
#include <bits/stdc++.h>
using namespace std;
const int N = 1000000 + 200;
const int inf = 0x7ffffff;
struct edge {
int from, to, w, next;
} e[N * 20];
int head[N];
int vis[N];
int dist[N];
int n, m, t, x;
void add(int i, int j, int w) {
e[t].from = i;
e[t].to = j;
e[t].w = w;
e[t].next = head[i];
head[i] = t++;
}
struct E {
int to, w;
E() {}
E(int to, int w) : to(to), w(w) {}
bool operator<(const E &other) const {
return w > other.w;
}
};
void dijkstra(int s) {
priority_queue<E> pq;
for (int i = 0; i <= n; ++i) {
dist[i] = inf;
}
dist[s] = 0;
pq.push(E(s, 0));
memset(vis, 0, sizeof vis);
while (!pq.empty()) {
int u = pq.top().to;
pq.pop();
if (u == m)
return;
if (vis[u])
continue;
vis[u] = 1;
for (int i = head[u]; i != -1; i = e[i].next) {
int v = e[i].to;
if (!vis[v] && dist[v] > dist[u] + e[i].w) {
dist[v] = dist[u] + e[i].w;
pq.push(E(v, dist[v]));
}
}
}
}
int main() {
scanf("%d%d", &x, &m);
if (x > m)
n = x + 100;
else
n = m + 100;
t = 0;
memset(head, -1, sizeof(head));
for (int i = 0; i < n; ++i) {
add(i, i + 1, 1);
add(i + 1, i, 1);
add(i + 1, 0, 3);
for (int j = 2; i * j <= n && i != 0; ++j) add(i, i * j, 2 + 2 * j);
}
dijkstra(x);
// for (int i = 0; i <= n - 100; ++i) printf("%d %d\n", i, dist[i]);
printf("%d\n", dist[m]);
return 0;
}
I. LTS and rectangular area union
题意
给出
给出的矩形的下底边位于同一水平线上即左下角为
思路
既然高度非递增,那么说明第
也就是说当处理到第
会发生变化的是哪一部分?其实就是
那么可以用 set
维护一个 set
中与
显然每个二元组只会被加入一次,合并一次。
时间复杂度
也可以用线段树来完成这个操作。
Code
#include <bits/stdc++.h>
using namespace std;
#define endl "\n"
#define fi first
#define se second
#define SZ(x) ((int)(x).size())
#define mkp make_pair
#define all(x) (x).begin(), (x).end()
using db = double;
using ll = long long;
using ull = unsigned long long;
using pII = pair<int, int>;
using pLL = pair<ll, ll>;
constexpr int mod = 998244353;
template <class T1, class T2>
inline void chadd(T1 &x, T2 y, int Mod = mod) {
x += y;
while (x >= Mod) x -= Mod;
while (x < 0) x += Mod;
}
template <class T1, class T2>
inline void chmax(T1 &x, T2 y) {
if (x < y)
x = y;
}
template <class T1, class T2>
inline void chmin(T1 &x, T2 y) {
if (x > y)
x = y;
}
inline int nextInt() {
int x;
cin >> x;
return x;
}
void rd() {}
template <class T, class... Ts>
void rd(T &arg, Ts &...args) {
cin >> arg;
rd(args...);
}
#define dbg(x...) \
do { \
cout << "\033[32;1m" << #x << " -> "; \
err(x); \
} while (0)
void err() {
cout << "\033[39;0m" << endl;
}
template <class T, class... Ts>
void err(const T &arg, const Ts &...args) {
cout << arg << ' ';
err(args...);
}
template <template <typename...> class T, typename t, typename... A>
void err(const T<t> &arg, const A &...args) {
for (auto &v : arg) cout << v << ' ';
err(args...);
}
void ptt() {
cout << endl;
}
template <class T, class... Ts>
void ptt(const T &arg, const Ts &...args) {
cout << ' ' << arg;
ptt(args...);
}
template <class T, class... Ts>
void pt(const T &arg, const Ts &...args) {
cout << arg;
ptt(args...);
}
void pt() {}
template <template <typename...> class T, typename t, typename... A>
void pt(const T<t> &arg, const A &...args) {
for (int i = 0, sze = arg.size(); i < sze; ++i) cout << arg[i] << " \n"[i == sze - 1];
pt(args...);
}
inline ll qpow(ll base, ll n) {
assert(n >= 0);
ll res = 1;
while (n) {
if (n & 1)
res = res * base % mod;
base = base * base % mod;
n >>= 1;
}
return res;
}
// head
constexpr int N = 1e6 + 10;
int n;
struct E {
ll l, r, h;
E() {}
E(ll l, ll r, ll h) : l(l), r(r), h(h) {}
bool operator<(const E &other) const {
return l < other.l;
}
} e[N];
void run() {
rd(n);
for (int i = 1; i <= n; ++i) rd(e[i].l, e[i].r, e[i].h);
ll res = 1;
ll area = 0;
set<E> se;
for (int i = 1; i <= n; ++i) {
ll l = e[i].l, r = e[i].r, h = e[i].h;
if (se.empty()) {
se.insert(E(l, r, h));
chadd(area, (r - l) * h % mod);
} else {
auto pos = se.upper_bound(E(l, l, 0));
if (pos != se.begin())
pos = prev(pos);
vector<E> vec;
while (pos != se.end()) {
auto nx = next(pos);
if ((pos->l >= l && pos->l <= r) || (pos->r >= l && pos->r <= r) || (l >= pos->l && l <= pos->r) ||
(r >= pos->l && r <= pos->r)) {
vec.push_back(*pos);
se.erase(pos);
pos = nx;
} else {
if (pos->l > r)
break;
else
pos = nx;
}
}
if (vec.empty()) {
se.insert(E(l, r, h));
chadd(area, (r - l) * h % mod);
} else {
ll _l = l, _r = r;
for (auto &it : vec) {
chmin(_l, it.l);
chmax(_r, it.r);
chadd(area, mod - (min(r, it.r) - max(l, it.l)) * h % mod);
}
chadd(area, (r - l) * h % mod);
se.insert(E(_l, _r, h));
}
}
res = res * area % mod;
}
pt(res);
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
cout.tie(nullptr);
cout << fixed << setprecision(20);
int _T = 1;
while (_T--) run();
return 0;
}
J. Hsueh- owns large quantities of apples
题意
有
- 第一个孩子拿到了
个苹果,他吃掉了一下,发现剩下的苹果能恰好分成 堆,他拿走了 堆,将剩下的一堆给第二个孩子。 - 第二个孩子拿到了
个苹果,吃掉了一个,发现剩下的苹果能恰好分成 堆,他拿走了 堆,将剩下的一堆给第三个孩子。 - 第
个孩子拿到了第 个孩子给他的苹果,吃掉了一个,发现剩下的苹果能恰好分成 堆,拿走了 堆,将剩下的一堆给第 个孩子。 - 最后一个孩子拿到了倒数第二个孩子给他的苹果,吃掉了一个,发现剩下的苹果能恰好分成
堆,拿走了 堆,然后留下了一堆,就离开了。
思路
考虑最后一个孩子从倒数第二个孩子得到的苹果数量为
那么只要让
显然
Code
#include <bits/stdc++.h>
using namespace std;
#define endl "\n"
#define fi first
#define se second
#define SZ(x) ((int)(x).size())
#define mkp make_pair
#define all(x) (x).begin(), (x).end()
using db = double;
using ll = long long;
using ull = unsigned long long;
using pII = pair<int, int>;
using pLL = pair<ll, ll>;
constexpr int mod = 998244353;
template <class T1, class T2>
inline void chadd(T1 &x, T2 y, int Mod = mod) {
x += y;
while (x >= Mod) x -= Mod;
while (x < 0) x += Mod;
}
template <class T1, class T2>
inline void chmax(T1 &x, T2 y) {
if (x < y)
x = y;
}
template <class T1, class T2>
inline void chmin(T1 &x, T2 y) {
if (x > y)
x = y;
}
inline int nextInt() {
int x;
cin >> x;
return x;
}
void rd() {}
template <class T, class... Ts>
void rd(T &arg, Ts &...args) {
cin >> arg;
rd(args...);
}
#define dbg(x...) \
do { \
cout << "\033[32;1m" << #x << " -> "; \
err(x); \
} while (0)
void err() {
cout << "\033[39;0m" << endl;
}
template <class T, class... Ts>
void err(const T &arg, const Ts &...args) {
cout << arg << ' ';
err(args...);
}
template <template <typename...> class T, typename t, typename... A>
void err(const T<t> &arg, const A &...args) {
for (auto &v : arg) cout << v << ' ';
err(args...);
}
void ptt() {
cout << endl;
}
template <class T, class... Ts>
void ptt(const T &arg, const Ts &...args) {
cout << ' ' << arg;
ptt(args...);
}
template <class T, class... Ts>
void pt(const T &arg, const Ts &...args) {
cout << arg;
ptt(args...);
}
void pt() {}
template <template <typename...> class T, typename t, typename... A>
void pt(const T<t> &arg, const A &...args) {
for (int i = 0, sze = arg.size(); i < sze; ++i) cout << arg[i] << " \n"[i == sze - 1];
pt(args...);
}
inline ll qpow(ll base, ll n) {
assert(n >= 0);
ll res = 1;
while (n) {
if (n & 1)
res = res * base % mod;
base = base * base % mod;
n >>= 1;
}
return res;
}
// head
constexpr int N = 1e2 + 10;
ll m, x;
struct M {
int a[5][5];
M() {
memset(a, 0, sizeof a);
}
M operator*(const M &other) const {
M res = M();
for (int i = 1; i <= 2; ++i) {
for (int j = 1; j <= 2; ++j) {
for (int k = 1; k <= 2; ++k) {
chadd(res.a[i][j], 1ll * a[i][k] * other.a[k][j] % mod);
}
}
}
return res;
}
} base, res;
void qpow(ll n) {
while (n) {
if (n & 1)
res = res * base;
base = base * base;
n >>= 1;
}
pt(res.a[1][1]);
}
void run() {
rd(m, x);
base = M();
res = M();
base.a[1][1] = x;
base.a[2][1] = 1;
base.a[2][2] = 1;
res.a[1][1] = 1;
res.a[1][2] = 1;
qpow(m);
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
cout.tie(nullptr);
cout << fixed << setprecision(20);
int _T = nextInt();
while (_T--) run();
// for (int kase = 1; kase <= _T; ++kase) {
// cout << "Case #" << kase << ": ";
// run();
// }
// while (cin >> n) run();
// run();
return 0;
}
但其实敲个矩阵快速幂,代码还是比较长的。
我们回顾解法,抽象一下,其实就是求这个东西:
令
我们推一推发现:
其实就是等比数列求和。
K. LTS buy wine
题意
给出
思路
Code
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
const int N = 2000 + 10;
ll a[N];
ll f[N][N];
int main() {
int n;
scanf("%d", &n);
for (int i = 1; i <= n; ++i) {
scanf("%lld", &a[i]);
f[i][i] = a[i] * n;
}
for (int len = 2; len <= n; ++len)
for (int i = 1; i <= n - len + 1; ++i) {
int j = i + len - 1;
ll x = f[i][j - 1] + a[j] * (n - (j - i + 1) + 1);
ll y = f[i + 1][j] + a[i] * (n - (j - i + 1) + 1);
if (x > y)
f[i][j] = x;
else
f[i][j] = y;
}
printf("%lld\n", f[1][n]);
return 0;
}
L. Line problem
题意
问二维平面上两个线段相交长度,相交一点则相交长度为
思路
本题定义为几何签到题
首先两条线段所在直线需要重合,否则输出 0
方法一
当两条线段重合的时候就可以转化为一维几何来解决
方法二
若答案大于 0,求两条线段四个端点的两两最长距离,然后减去两条线段长度即可(需要注意精度)
Code
#include <bits/stdc++.h>
using namespace std;
#define dbg(x...) \
do { \
cout << #x << " -> "; \
err(x); \
} while (0)
void err() {
cout << endl;
}
template <class T, class... Ts>
void err(const T &arg, const Ts &...args) {
cout << arg << ' ';
err(args...);
}
#define endl "\n"
#define all(A) A.begin(), A.end()
using ll = long long;
using db = double;
using pII = pair<int, int>;
const int INF = 0x3f3f3f3f;
const ll INFLL = 0x3f3f3f3f3f3f3f3f;
const int N = 2e5 + 10;
const db eps = 1e-8;
int sgn(db x) {
if (fabs(x) < eps)
return 0;
else
return x > 0 ? 1 : -1;
}
struct Point {
db x, y;
Point() {}
Point(db _x, db _y) {
x = _x, y = _y;
}
void input() {
cin >> x >> y;
}
Point operator+(const Point &b) const {
return Point(x + b.x, y + b.y);
}
Point operator-(const Point &b) const {
return Point(x - b.x, y - b.y);
}
double operator^(const Point &b) const {
return x * b.y - y * b.x;
}
double operator*(const Point &b) const {
return x * b.x + y * b.y;
}
double distance(Point p) {
return hypot(x - p.x, y - p.y);
}
} dir;
struct Line {
Point s, e;
Line() {}
Line(Point _s, Point _e) {
s = _s;
e = _e;
}
void input() {
s.input();
e.input();
}
bool parallel(Line v) {
return sgn((e - s) ^ (v.e - v.s)) == 0;
}
int relation(Point p) {
int c = sgn((p - s) ^ (e - s));
if (c < 0)
return 1;
else if (c > 0)
return 2;
else
return 3;
}
int linecrossline(Line v) {
if ((*this).parallel(v))
return v.relation(s) == 3;
return 2;
}
} l1, l2;
db gao(Point a, Point b) {
Point d = b - a;
return sgn(dir * d) * a.distance(b);
}
void RUN() {
l1.input();
l2.input();
if (l1.linecrossline(l2) != 1) {
cout << 0 << endl;
return;
}
dir = l1.e - l1.s;
db a = 0, b = gao(l1.s, l1.e), c = gao(l1.s, l2.s), d = gao(l1.s, l2.e);
if (a > b)
swap(a, b);
if (c > d)
swap(c, d);
db res = 0;
if (sgn(b - c) <= 0) {
res = 0;
} else if (sgn(b - c) >= 0 && sgn(b - d) <= 0) {
res = min(b - c, b - a);
} else {
if (sgn(a - c) <= 0) {
res = d - c;
} else if (sgn(a - c) >= 0 && sgn(a - d) <= 0) {
res = d - a;
}
}
cout << res << endl;
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr), cout.tie(nullptr);
cout << fixed << setprecision(20);
int _T;
cin >> _T;
while (_T--) {
RUN();
}
return 0;
}
M. Rikka with Random Graph
题意
给出一个
图的边集是通过一个「随机」的方法生成。
强制在线。
思路
这个题的做法,就是强连通缩点之后变成 DAG
,然后根据拓扑序使用 bitset
进行转移可达关系即可。
那空间需要多大?
需要
但其实采用分块的做法,可以降低空间复杂度,但是需要离线解决询问。
那么我们的想法是,随机图是否具有最大的弱连通分量大小较小的性质?
我们在随机很多次后发现,随机图好像没有这个性质,但是我们不想放弃我们提出的解法,于是手动构造了一个「随机图」,使得它满足这种性质。
wnext()
函数用于生成具有偏移期望的随机值,当
并且我们控制了随机的上界,使得这个图的连边不会到处交叉连边,而是会呈现一个块状。最终弱连通分量的最大大小被降低。
虽然我们不会证明,但是期望嘛,只要多跑几次取均值就可以看成期望了吧(逃
经过简单的随机发现,当
那么我们就可以每个弱连通分量重新编号后分别做,这样需要的空间是:
绰绰有余。
那既然弱连通分量的 size
不大,那么我对每次询问,直接 bfs
行不行?
可以是可以,而且常数小,复杂度看上去也不大,也就
我也注意到了这种做法,但是用 bitset
的做法比较快,所以把这种做法卡掉了,所以时限可能有点紧,大概
我们令
Code
#include <bits/stdc++.h>
using namespace std;
#define endl "\n"
#define fi first
#define se second
#define SZ(x) ((int)(x).size())
#define mkp make_pair
#define all(x) (x).begin(), (x).end()
using db = double;
using ll = long long;
using ull = unsigned long long;
using pII = pair<int, int>;
using pLL = pair<ll, ll>;
constexpr int mod = 1e9 + 7;
template <class T1, class T2>
inline void chadd(T1 &x, T2 y, int Mod = mod) {
x += y;
while (x >= Mod) x -= Mod;
while (x < 0) x += Mod;
}
template <class T1, class T2>
inline void chmax(T1 &x, T2 y) {
if (x < y)
x = y;
}
template <class T1, class T2>
inline void chmin(T1 &x, T2 y) {
if (x > y)
x = y;
}
inline int nextInt() {
int x;
cin >> x;
return x;
}
void rd() {}
template <class T, class... Ts>
void rd(T &arg, Ts &...args) {
cin >> arg;
rd(args...);
}
#define dbg(x...) \
do { \
cout << "\033[32;1m" << #x << " -> "; \
err(x); \
} while (0)
void err() {
cout << "\033[39;0m" << endl;
}
template <class T, class... Ts>
void err(const T &arg, const Ts &...args) {
cout << arg << ' ';
err(args...);
}
template <template <typename...> class T, typename t, typename... A>
void err(const T<t> &arg, const A &...args) {
for (auto &v : arg) cout << v << ' ';
err(args...);
}
void ptt() {
cout << endl;
}
template <class T, class... Ts>
void ptt(const T &arg, const Ts &...args) {
cout << ' ' << arg;
ptt(args...);
}
template <class T, class... Ts>
void pt(const T &arg, const Ts &...args) {
cout << arg;
ptt(args...);
}
void pt() {}
template <template <typename...> class T, typename t, typename... A>
void pt(const T<t> &arg, const A &...args) {
for (int i = 0, sze = arg.size(); i < sze; ++i) cout << arg[i] << " \n"[i == sze - 1];
pt(args...);
}
inline ll qpow(ll base, ll n) {
assert(n >= 0);
ll res = 1;
while (n) {
if (n & 1)
res = res * base % mod;
base = base * base % mod;
n >>= 1;
}
return res;
}
// head
unsigned long long k1, k2;
int n, m, q, _u[100001], _v[100001];
unsigned long long xorShift128Plus() {
unsigned long long k3 = k1, k4 = k2;
k1 = k4;
k3 ^= k3 << 23;
k2 = k3 ^ k4 ^ (k3 >> 17) ^ (k4 >> 26);
return k2 + k4;
}
int wnext(int l, int r, int t) {
int res = xorShift128Plus() % (r - l + 1) + l;
for (int i = 1; i < t; ++i) {
res = max(res, int(xorShift128Plus() % (r - l + 1) + l));
}
return res;
}
void gen() {
int S = min(1000, n);
for (int i = 1; i <= m; ++i) {
_u[i] = wnext(1, min(n, ((i % S) + 1) * S), 50);
_v[i] = wnext(1, min(n, ((i % S) + 1) * S), 50);
// dbg(_u[i], _v[i]);
}
}
const int N = 1e5 + 10;
pII pid[N];
struct UFS {
int fa[N], sze[N];
void init(int n) {
for (int i = 1; i <= n; ++i) {
fa[i] = 0;
sze[i] = 1;
}
}
int find(int x) {
return fa[x] == 0 ? x : fa[x] = find(fa[x]);
}
bool merge(int x, int y) {
int fx = find(x), fy = find(y);
if (fx != fy) {
if (sze[fx] > sze[fy])
swap(fx, fy);
fa[fx] = fy;
sze[fy] += sze[fx];
return true;
}
return false;
}
} ufs;
struct Bitset {
using ull = unsigned long long;
static constexpr int W = 64;
int n;
vector<ull> bits;
void init(int _n) {
n = _n + 1;
bits = vector<ull>(n / W + 10, 0);
}
void Or(const Bitset &t) {
for (int i = 0; i <= n / W; ++i) bits[i] |= t.bits[i];
}
void set(int x) {
bits[x / W] |= 1llu << (x % W);
}
int ask(int x) {
return (bits[x / W] >> (x % W)) & 1;
}
};
struct Sol {
vector<vector<int>> G;
vector<Bitset> b;
int n;
vector<int> Low, DFN, sta, Belong;
int cntSCC, cntSta, cntLow;
vector<bool> Insta;
Sol(int _n) {
n = _n;
G.clear();
G.resize(n + 5);
Low = vector<int>(n + 5, 0);
DFN = vector<int>(n + 5, 0);
sta = vector<int>(n + 5, 0);
Belong = vector<int>(n + 5, 0);
Insta = vector<bool>(n + 5, false);
cntSCC = cntSta = cntLow = 0;
}
void dfs(int u) {
Low[u] = DFN[u] = ++cntLow;
sta[++cntSta] = u;
Insta[u] = 1;
for (auto &v : G[u]) {
if (!DFN[v]) {
dfs(v);
Low[u] = min(Low[u], Low[v]);
} else if (Insta[v])
Low[u] = min(Low[u], DFN[v]);
}
if (Low[u] == DFN[u]) {
++cntSCC;
int v;
do {
v = sta[cntSta--];
Insta[v] = 0;
Belong[v] = cntSCC;
} while (v != u);
}
}
void gao() {
for (int i = 1; i <= n; ++i)
if (!DFN[i])
dfs(i);
b.resize(cntSCC + 5);
for (int i = 1; i <= cntSCC; ++i) b[i].init(cntSCC + 5), b[i].set(i);
vector<vector<int>> T;
T.clear();
T.resize(cntSCC + 5);
for (int u = 1; u <= n; ++u) {
for (auto &v : G[u]) {
if (Belong[u] == Belong[v])
continue;
T[Belong[v]].push_back(Belong[u]);
}
}
for (int u = 1; u <= cntSCC; ++u) {
for (auto &v : T[u]) {
b[v].Or(b[u]);
}
}
}
int query(int u, int v) {
if (Belong[u] == Belong[v])
return 1;
// dbg(u, v);
return b[Belong[u]].ask(Belong[v]);
}
};
vector<Sol> sol;
void numerAS() {
sol.clear();
vector<vector<int>> G, vec;
vec.clear();
G.clear();
vec.resize(n + 1);
G.resize(n + 1);
ufs.init(n);
for (int i = 1; i <= m; ++i) {
int u = _u[i], v = _v[i];
if (u != v) {
G[u].push_back(v);
ufs.merge(u, v);
// dbg(u, v);
}
}
for (int i = 1; i <= n; ++i)
if (!G[i].empty()) {
sort(all(G[i]));
G[i].erase(unique(all(G[i])), G[i].end());
}
for (int i = 1; i <= n; ++i) {
vec[ufs.find(i)].push_back(i);
}
int _pid = -1;
for (auto &ve : vec)
if (!ve.empty()) {
sol.push_back(Sol(SZ(ve)));
++_pid;
int nid = 0;
for (auto &it : ve) {
++nid;
pid[it] = pII(_pid, nid);
}
for (auto &it : ve) {
for (auto &v : G[it]) {
v = pid[v].se;
}
sol.back().G[pid[it].se] = G[it];
}
}
}
void run() {
rd(n, m, q, k1, k2);
gen();
numerAS();
for (auto &it : sol) {
it.gao();
}
for (int i = 1, u, v; i <= q; ++i) {
rd(u, v);
// dbg(pid[u].fi, pid[u].se, pid[v].se);
int res = 0;
if (pid[u].fi == pid[v].fi) {
res = sol[pid[u].fi].query(pid[u].se, pid[v].se);
}
pt(res ? "Yes" : "No");
cout.flush();
}
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
cout.tie(nullptr);
cout << fixed << setprecision(20);
int _T = 1;
while (_T--) run();
// for (int kase = 1; kase <= _T; ++kase) {
// cout << "Case #" << kase << ": ";
// run();
// }
// while (cin >> n) run();
// run();
return 0;
}
Created: March 24, 2022