[题解]数学学科竞赛
2022-03-05 数学学科竞赛
令人振奋的是,T1 正确地感觉出了结论ヾ(≧▽≦*)o
小 G 的约数 / Divisor
第一个和第二个条件都很显然,对于第三个条件变个形
也就是说,.
因此,我们需要构造的数列满足:
- 所有 都是 的因数;
- ;
- 数列 单调不降;
单调不降可以不管他,只要我们选出了 ,排序之后就可以得到 . 事实上,现在我们要在 的因数中选择最长反链,答案就是最长反链长度,一个比较直观的结论是选择 “按照质因数指数之和分层之后” 的最大的一层。这个结论用调整法稍微想一下感觉差不多是对的。
小 G 的连通图 / Graph
这个我还不会🤕.
这个思路是怎么想到的🤕.
两个数 可以连边当且仅当存在 使得 ,那么,我们可以耍赖地取一个 ,这样, 和 都可以有连边,因为 ,唯一一个钉子户就是 ,这个时候,我们想个办法,从 的质数集中随意取出一个 ,使得 (对于其他质数仍然存在 ),这样 ,并且 ,于是 就和 联通了,这个 不难找,令 ,于是 满足 即可。
但是又有一个问题,就是 (不是 的原因是 中如果含有其他质数,那么可以通过该质数与 间接相连),也就是 和 又不连通了,这个时候,我们又会想取出另外一个质数 ,使得 ,用类似填上 的坑,但是有一个问题,那就是 不意味着 能和 联通,于是我们规定前面取出的 ,这样在区间 中就不存在 这个点,于是不用考虑它了,然后我们用 填上了 的这个坑。
然而,理想是十分美好的,用这个 填上 的坑之后,又发现 也出现了坑(不是 是因为 ,因此也不存在 这样的点了),如果再用类似方法,我们将无限递归下去,直到重新回到 ,打破这个无尽递归的方法是,我们让 和 互成嵌套,即 可以填 的坑,而 可以填 的坑,即需要满足几个取模方程
通过 发现他们的必要条件是 (因为我们要使 和 联通,进而和 联通,因此 不能取 ),于是我们只需要取 ,即令 ,然后满足 两个方程即可。
总结一下,我们需要干的事情:算出 ,找到一个 使得 均为质数,然后找到一个 使得 ,这个 就是我们想要求的答案,至于怎么找到 ,稍微变个形:
暴力找即可。
另外,不难证明打表发现在 时,总能找到一个 使得 均为质数且有一个合法的 . 所以较小范围还得写个暴力判断一下。
至于题解做法,见鬼去吧。
/** @author __Elaina__ */
#pragma GCC optimize(2)
#include <bits/stdc++.h>
using namespace std;
#define USING_FREAD
// #define NDEBUG
#include <cassert>
namespace Elaina {
/** その可憐な少女は魔女であり、旅人でした。 ―― そう、私です! */
#define rep(i, l, r) for(int i = (l), i##_end_ = (r); i <= i##_end_; ++i)
#define drep(i, l, r) for(int i = (l), i##_end_ = (r); i >= i##_end_; --i)
#define fi first
#define se second
#define mp(a, b) make_pair(a, b)
#define Endl putchar('\n')
#define whole(v) ((v).begin()), ((v).end())
#define bitcnt(s) (__builtin_popcount(s))
/** @warning no forced type conversion */
#define rqr(x) ((x) * (x))
#define y0 FUCK_UP
#define y1 MOTHER_FUCKER
#define console(...) fprintf(stderr, __VA_ARGS__)
typedef long long ll;
typedef unsigned long long ull;
typedef std::pair<int, int> pii;
template<class T> inline T fab(T x) { return x < 0 ? -x : x; }
template<class T> inline void chkmin(T& x, const T rhs) { x = std::min(x, rhs); }
template<class T> inline void chkmax(T& x, const T rhs) { x = std::max(x, rhs); }
template<class T> inline void myswap(T& x, T& y) { x ^= y ^= x ^= y; }
#ifdef USING_FREAD
inline char qkgetc() {
# define BUFFERSIZE 1 << 20
static char BUF[BUFFERSIZE], *p1 = BUF, *p2 = BUF;
return p1 == p2 && (p2 = (p1 = BUF) + fread(BUF, 1, BUFFERSIZE, stdin), p1 == p2) ? EOF : *p1++;
# undef BUFFERSIZE
}
# define CHARRECEI qkgetc()
#else
# define CHARRECEI getchar()
#endif
template<class T> inline T readret(T x) {
x = 0; int f = 0; char c;
while (!isdigit(c = CHARRECEI)) if(c == '-') f = 1;
for (x = (c ^ 48); isdigit(c = CHARRECEI); x = (x << 1) + (x << 3) + (c ^ 48));
return f ? -x : x;
}
template<class T> inline void readin(T& x) {
x = 0; int f = 0; char c;
while (!isdigit(c = CHARRECEI)) if (c == '-') f = 1;
for (x = (c ^ 48); isdigit(c = CHARRECEI); x = (x << 1) + (x << 3) + (c ^ 48));
if (f) x = -x;
}
template<class T, class... Args> inline void readin(T& x, Args&... args) {
readin(x), readin(args...);
}
template<class T> inline void writln(T x, char c = '\n') {
if (x < 0) putchar('-'), x = -x;
static int __stk[55], __bit = 0;
do __stk[++__bit] = x % 10, x /= 10; while (x);
while (__bit) putchar(__stk[__bit--] ^ 48);
putchar(c);
}
} // namespace Elaina
using namespace Elaina;
int n;
namespace Brute {
const int Maxn = 2e7;
int prime[Maxn + 5], minp[Maxn + 5];
bool vis[Maxn + 5];
inline void sieve() {
vis[1] = true;
rep (i, 2, Maxn) {
if (!vis[i]) prime[++*prime] = minp[i] = i;
for (int j = 1; j <= *prime && i * prime[j] <= Maxn; ++j) {
vis[i * prime[j]] = true;
minp[i * prime[j]] = prime[j];
if (i % prime[j] == 0) break;
}
}
}
inline bool check(int l) {
for (int i = l, j = 1; j <= n; ++i, ++j)
if (i % 2 == 1 && j - minp[i] < 1 && j + minp[i] > n)
return false;
return true;
}
inline void launch() {
sieve();
for (int l = 2; l <= Maxn - n + 1; ++l) if (check(l))
return void(printf("%d\n", l));
puts("No solution");
}
} // namespace Brute;
const int Maxn = 1e5;
const int Bit = 1e8;
int prime[Maxn + 5];
bool vis[Maxn + 5];
inline void sieve() {
vis[1] = true;
rep (i, 2, Maxn) {
if (!vis[i]) prime[++*prime] = i;
for (int j = 1; j <= *prime && i * prime[j] <= Maxn; ++j) {
vis[i * prime[j]] = true;
if (i % prime[j] == 0) break;
}
}
}
struct Bigint {
int len;
ll a[Maxn + 5];
inline Bigint operator =(const int rhs) {
assert(rhs < Bit);
memset(a, 0, sizeof a);
len = 1, a[0] = rhs;
return (*this);
}
inline Bigint(const int rhs) { (*this) = rhs; }
// inline Bigint operator *(const int rhs) const {
// Bigint ret = (*this);
// for (int i = 0; i < len; ++i) ret.a[i] *= rhs;
// for (int i = 0; i < len; ++i)
// ret.a[i + 1] += ret.a[i] / Bit, ret.a[i] %= Bit;
// if (ret.a[len]) ++ret.len;
// return ret;
// }
inline void operator *=(const int& rhs) {
for (int i = 0; i < len; ++i) a[i] *= rhs;
for (int i = 0; i < len; ++i)
a[i + 1] += a[i] / Bit, a[i] %= Bit;
if (a[len]) ++len;
}
inline void print() {
printf("%lld", a[len - 1]);
for (int i = len - 2; i >= 0; --i) printf("%08lld", a[i]);
Endl;
}
};
signed main() {
freopen("graph.in", "r", stdin);
freopen("graph.out", "w", stdout);
readin(n);
if (n == 1) return puts("1") & 0;
if (n <= 40) return Brute::launch(), 0;
sieve();
int a;
for (a = 1; a <= n; ++a) {
if ((a << 1) + 1 > n) break;
if (1ll * a * a <= n) continue;
if (!vis[a] && !vis[(a << 1) + 1]) break;
}
Bigint ans = 1; ll M = 1ll * a * ((a << 1) + 1), prod = 1;
for (int i = 1; i <= n; ++i) if (!vis[i] && i != a && i != (a << 1) + 1)
prod = prod * i % M, ans *= i;
int times; ll now;
for (now = prod, times = 1; now != M - 3 * a - 1; now = (now + prod) % M, ++times);
ans *= times;
ans.print();
return 0;
}
小 G 的 DAG / Dag
把带 的算法几乎全部遍历了一遍,然后不会,分块几乎都是往大小点想,完全没想到对操作序列进行分块......不过确实应当往这个方面想一下的,对于一个点,操作 1 显然只有最后一次才真正有效,就凭这一点,也应当想一想操作分块。
对于操作 ,可以设置一个阈值 ,当这种操作堆到 大小的时候再全部同时操作,对于剩下的,可以在询问的时候直接暴力扫,这个部分的复杂度是 .
对于操作 2,实际上对于一个点 ,真正有用的操作 2 是介于最后一个覆盖它的 1 操作和询问它的 3 操作之间,也就是说,我们需要处理出一个操作区间内,能够到达 的最小值。因此,我们需要处理出 ,即第 个块对于 的最小值是多少(如果没有,就设为 ),处理询问的时候,边角料就暴力问了。
现在唯一的问题就是处理出 能否到 这个问题了,直接开 的 bitset
怕是有点困难,于是乎,把点集每 分成一组,处理出每个点到这 个点的可达情况,这样空间大概就只需要 了。
最后的复杂度大概是 吧,效率还行。
/** @author __Elaina__ */
#include <bits/stdc++.h>
using namespace std;
#define USING_FREAD
// #define NDEBUG
#include <cassert>
namespace Elaina {
/** その可憐な少女は魔女であり、旅人でした。 ―― そう、私です! */
#define rep(i, l, r) for(int i = (l), i##_end_ = (r); i <= i##_end_; ++i)
#define drep(i, l, r) for(int i = (l), i##_end_ = (r); i >= i##_end_; --i)
#define fi first
#define se second
#define mp(a, b) make_pair(a, b)
#define Endl putchar('\n')
#define whole(v) ((v).begin()), ((v).end())
#define bitcnt(s) (__builtin_popcount(s))
/** @warning no forced type conversion */
#define rqr(x) ((x) * (x))
#define y0 FUCK_UP
#define y1 MOTHER_FUCKER
#define console(...) fprintf(stderr, __VA_ARGS__)
typedef long long ll;
typedef unsigned long long ull;
typedef std::pair<int, int> pii;
template<class T> inline T fab(T x) { return x < 0 ? -x : x; }
template<class T> inline void chkmin(T& x, const T rhs) { x = std::min(x, rhs); }
template<class T> inline void chkmax(T& x, const T rhs) { x = std::max(x, rhs); }
template<class T> inline void myswap(T& x, T& y) { x ^= y ^= x ^= y; }
#ifdef USING_FREAD
inline char qkgetc() {
# define BUFFERSIZE 1 << 20
static char BUF[BUFFERSIZE], *p1 = BUF, *p2 = BUF;
return p1 == p2 && (p2 = (p1 = BUF) + fread(BUF, 1, BUFFERSIZE, stdin), p1 == p2) ? EOF : *p1++;
# undef BUFFERSIZE
}
# define CHARRECEI qkgetc()
#else
# define CHARRECEI getchar()
#endif
template<class T> inline T readret(T x) {
x = 0; int f = 0; char c;
while (!isdigit(c = CHARRECEI)) if(c == '-') f = 1;
for (x = (c ^ 48); isdigit(c = CHARRECEI); x = (x << 1) + (x << 3) + (c ^ 48));
return f ? -x : x;
}
template<class T> inline void readin(T& x) {
x = 0; int f = 0; char c;
while (!isdigit(c = CHARRECEI)) if (c == '-') f = 1;
for (x = (c ^ 48); isdigit(c = CHARRECEI); x = (x << 1) + (x << 3) + (c ^ 48));
if (f) x = -x;
}
template<class T, class... Args> inline void readin(T& x, Args&... args) {
readin(x), readin(args...);
}
template<class T> inline void writln(T x, char c = '\n') {
if (x < 0) putchar('-'), x = -x;
static int __stk[55], __bit = 0;
do __stk[++__bit] = x % 10, x /= 10; while (x);
while (__bit) putchar(__stk[__bit--] ^ 48);
putchar(c);
}
} // namespace Elaina
using namespace Elaina;
const int Maxn = 1e5;
const int Maxl = 1e4;
const int Blo = 320;
int op[Maxn + 5], u[Maxn + 5], x[Maxn + 5], qid[Maxn + 5];
int deg[Maxn + 5];
vector<int> g[Maxn + 5];
int n, m, q;
int bel[Maxn + 5], bl[Maxn + 5], br[Maxn + 5], bcnt;
inline void input() {
readin(n, m, q);
int a, b;
rep (i, 1, m) {
readin(a, b);
if (a == b) continue;
g[a].push_back(b), ++deg[b];
}
rep (i, 1, q) {
readin(op[i], u[i]);
if (op[i] ^ 3) readin(x[i]);
else qid[i] = ++*qid;
}
}
int que[Maxn + 5], hd, tl;
inline void Topo() {
hd = 1;
rep (i, 1, n) if (!deg[i]) que[++tl] = i;
while (hd <= tl) {
int u = que[hd++];
for (const int& v: g[u]) if (!--deg[v])
que[++tl] = v;
}
// rep (i, 1, tl) console("%d%c", que[i], " \n"[i == tl]);
assert(tl == n);
}
int ans[Maxn + 5];
int enval[Maxn / Blo + 5][Maxn + 5], enmin[Maxn / Blo + 5][Maxn + 5];
int lasmodi[Maxn + 5];
bitset<Maxl + 5> to[Maxn + 5];
inline void solve(int ql, int qr) {
drep (i, n, 1) {
int u = que[i]; to[u].reset(); // don't forget to clear!!
if (ql <= u && u <= qr) to[u].set(u - ql);
for (const int& v: g[u]) to[u] |= to[v];
}
rep (id, 1, bcnt) {
rep (i, 1, n) lasmodi[i] = enval[id - 1][i];
rep (i, bl[id], br[id]) {
if (op[i] == 3 && ql <= u[i] && u[i] <= qr) {
rep (j, bl[id], i) if (op[j] == 1 && to[u[j]][u[i] - ql])
lasmodi[u[i]] = j;
// console("lasmodi == %d\n", lasmodi[u[i]]);
int cur = x[lasmodi[u[i]]];
// console("cur == %d\n", cur);
// No ST table !!!
// console("l == %d, r == %d\n", max(lasmodi[u[i]] + 1, bl[id]), i - 1);
rep (j, max(lasmodi[u[i]] + 1, bl[id]), i - 1)
if (op[j] == 2 && to[u[j]][u[i] - ql])
chkmin(cur, x[j]);
// console("bl == %d, br == %d\n", bel[lasmodi[u[i]]] + 1, id - 1);
rep (j, bel[lasmodi[u[i]]] + 1, id - 1)
chkmin(cur, enmin[j][u[i]]);
if (bel[lasmodi[u[i]]] != id && lasmodi[u[i]] > 0)
rep (j, lasmodi[u[i]] + 1, br[bel[lasmodi[u[i]]]])
if (op[j] == 2 && to[u[j]][u[i] - ql])
chkmin(cur, x[j]);
ans[qid[i]] = cur;
lasmodi[u[i]] = enval[id - 1][u[i]];
}
}
}
}
inline void prelude() {
for (int x = 1; (x - 1) * Blo + 1 <= q; ++x) {
bcnt = x;
bl[x] = (x - 1) * Blo + 1;
br[x] = min(x * Blo, q);
rep (i, bl[x], br[x]) bel[i] = x;
}
rep (i, 1, bcnt) {
rep (j, 1, n) enval[i][j] = enval[i - 1][j];
memset(enmin[i] + 1, 0x3f, n << 2);
rep (j, bl[i], br[i]) {
if (op[j] == 1) enval[i][u[j]] = j;
else if (op[j] == 2) chkmin(enmin[i][u[j]], x[j]);
// console("udp:> enval[%d, %d] == %d, enmin[%d, %d] == %d\n", i, u[j], enval[i][u[j]], i, u[j], enmin[i][u[j]]);
}
rep (j, 1, n) {
int u = que[j];
for (const int& v: g[u]) {
// printf("When u == %d, v == %d\n", u, v);
chkmax(enval[i][v], enval[i][u]);
chkmin(enmin[i][v], enmin[i][u]);
// console("udp:> enval[%d, %d] == %d, enmin[%d, %d] == %d\n", i, v, enval[i][v], i, v, enmin[i][v]);
}
}
}
}
signed main() {
freopen("dag.in", "r", stdin);
freopen("dag.out", "w", stdout);
// freopen("_log.out", "w", stderr);
input();
Topo();
prelude();
// console("finished prelude\n");
// rep (i, 1, bcnt) rep (j, 1, n)
// console("enval[%d, %d] == %d, enmin[%d, %d] == %d\n", i, j, enval[i][j], i, j, enmin[i][j]);
for (int i = 1; i <= Maxn / Maxl + 1; ++i) {
int ql = (i - 1) * Maxl + 1, qr = i * Maxl; chkmin(qr, n);
solve(ql, qr);
// console("solve [%d, %d]\n", ql, qr);
if (qr == n) break;
}
rep (i, 1, *qid) writln(ans[i]);
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现
2020-03-14 「模板」替罪羊树