[题解]数学学科竞赛

\[\color{red}{\text{校长者,真神人也,左马桶,右永神,会执利笔破邪炁,何人当之?}} \\ \begin{array}{|} \hline \color{pink}{\text{The principal is really a god}} \\ \color{pink}{\text{with a closestool on the left and Yongshen on the right}} \\ \color{pink}{\text{holding a sharp pen to pierce the truth}} \\ \color{pink}{\text{Who can resist him? }} \\ \hline \end{array} \\ \begin{array}{|} \hline \color{green}{\text{校長は本当に神であり、左側にトイレ、右側にヨンシェンがあり}} \\ \color{green}{\text{鋭いペンを持って真実を突き刺している。誰が彼に抵抗できるだろうか? }} \\ \hline \end{array} \\ \begin{array}{|} \hline \color{lightblue}{\text{Le principal est vraiment un dieu}} \\ \color{lightblue}{\text{avec des toilettes à gauche et Yongshen à droite}} \\ \color{lightblue}{\text{tenant un stylo pointu pour percer la vérité}} \\ \color{lightblue}{\text{Qui peut lui résister ? }} \\ \hline \end{array} \\ \begin{array}{|} \hline \color{purple}{\text{Der Direktor ist wirklich ein Gott}} \\ \color{purple}{\text{mit einer Toilette links und Yongshen rechts}} \\ \color{purple}{\text{der einen spitzen Stift hält}} \\ \color{purple}{\text{um die Wahrheit zu durchdringen.}} \\ \color{purple}{\text{Wer kann ihm widerstehen? }} \\ \hline \end{array} \\ \begin{array}{|} \hline \color{cyan}{\text{Principalis deus est, Yongshen a dextris cum latrina}} \\ \color{cyan}{\text{acuto stylo ad perforandum veritatem: quis resistet ei? }} \\ \hline \end{array} \\ \color{red}{\text{对曰:“无人,狗欲当之,还请赐教!”}} \\ \newcommand\brak[1]{\left({#1}\right)} \newcommand\Brak[1]{\left\{{#1}\right\}} \newcommand\d[0]{\text{d}} \newcommand\string[2]{\genfrac{\{}{\}}{0pt}{}{#1}{#2}} \newcommand\down[2]{{#1}^{\underline{#2}}} \newcommand\ddiv[2]{\left\lfloor\frac{#1}{#2}\right\rfloor} \newcommand\udiv[2]{\left\lceil\frac{#1}{#2}\right\rceil} \newcommand\lcm[0]{\operatorname{lcm}} \newcommand\set[1]{\left\{{#1}\right\}} \newcommand\ceil[1]{\left\lceil{#1}\right\rceil} \newcommand\floor[1]{\left\lfloor{#1}\right\rfloor} \newcommand\rhs[1]{\;\text{Rhs}\;#1} \newcommand\lhs[1]{\;\text{Lhs}\;#1} \newcommand\Vec[1]{\vec{\mathbf{#1}}} \newcommand\rank[0]{\text{rank}} \]




  \(\mathcal{Back\;To\;The\;Menu}\).

2022-03-05 数学学科竞赛

  令人振奋的是,T1 正确地感觉出了结论ヾ(≧▽≦*)o

小 G 的约数 / Divisor

  第一个和第二个条件都很显然,对于第三个条件变个形

\[\begin{aligned} d_i\lcm(d_i,d_j)+d_j\gcd(d_i,d_j)&>2d_id_j \\ \Leftrightarrow \frac{d_i^2d_j}{g}+d_jg&>2d_id_j&\gcd(d_i,d_j)\overset\Delta=g \\ \Leftrightarrow d_jg&>\brak{2-\frac{d_i}{g}}d_id_j \\ \Leftrightarrow \frac{g}{d_i}&>2-\frac{d_i}{g} \\ \Leftrightarrow \frac{d_i}{g}&>1 \\ \Rightarrow d_i&\neq g \end{aligned} \]

  也就是说,\(\forall i<j,d_i\nmid d_j\).

  因此,我们需要构造的数列满足:

  1. 所有 \(d_i\) 都是 \(n\) 的因数;
  2. \(\forall i<j,d_i\nmid d_j\)
  3. 数列 \(\set{D_i}\) 单调不降;

  单调不降可以不管他,只要我们选出了 \(\set{d_i}\),排序之后就可以得到 \(\set{D_i}\). 事实上,现在我们要在 \(n\) 的因数中选择最长反链,答案就是最长反链长度,一个比较直观的结论是选择 “按照质因数指数之和分层之后” 的最大的一层。这个结论用调整法稍微想一下感觉差不多是对的。

小 G 的连通图 / Graph

  这个我还不会🤕.

  这个思路是怎么想到的🤕.

  两个数 \(x,y\) 可以连边当且仅当存在 \(p\) 使得 \(x\equiv y\equiv 0\pmod p\),那么,我们可以耍赖地取一个 \(\displaystyle L=\prod_{p\in \Bbb P}^{p\le n}p\),这样,\(L\)\(L+2,L+3,\cdots,L+n-1\) 都可以有连边,因为 \(\forall 1<t\le n,L\equiv L+t\pmod{t}\),唯一一个钉子户就是 \(L+1\),这个时候,我们想个办法,从 \([1,n]\) 的质数集中随意取出一个 \(a\),使得 \(x\equiv -1\pmod a\)(对于其他质数仍然存在 \(p\mid x\)),这样 \(x+1\equiv 0\equiv x+a+1\pmod a\),并且 \(x+a+1\equiv x\equiv 0\pmod{a+1}\),于是 \(x+1\) 就和 \(x\) 联通了,这个 \(x\) 不难找,令 \(\displaystyle q=\frac{L}{a}\),于是 \(x\) 满足 \(q\mid x\land a\mid(x+1)\) 即可。

  但是又有一个问题,就是 \(x\equiv x+a^k\equiv -1\not\equiv 0\pmod a\)(不是 \(x+ka\) 的原因是 \(k\) 中如果含有其他质数,那么可以通过该质数与 \(x\) 间接相连),也就是 \(x+a^k\)\(x\) 又不连通了,这个时候,我们又会想取出另外一个质数 \(b\),使得 \(x\equiv -a\pmod b\),用类似填上 \(x+1\) 的坑,但是有一个问题,那就是 \(x\equiv -a\pmod b\) 不意味着 \(x\) 能和 \(x+a^2\) 联通,于是我们规定前面取出的 \(a>\sqrt n\),这样在区间 \([x,x+n-1]\) 中就不存在 \(x+a^2\) 这个点,于是不用考虑它了,然后我们用 \(b\) 填上了 \(x+a\) 的这个坑。

  然而,理想是十分美好的,用这个 \(b\) 填上 \(a\) 的坑之后,又发现 \(x+b\) 也出现了坑(不是 \(x+b^k\) 是因为 \(b>a>\sqrt n\),因此也不存在 \(x+b^2\) 这样的点了),如果再用类似方法,我们将无限递归下去,直到重新回到 \(a\),打破这个无尽递归的方法是,我们让 \(a\)\(b\) 互成嵌套,即 \(a\) 可以填 \(x+b\) 的坑,而 \(b\) 可以填 \(x+a\) 的坑,即需要满足几个取模方程

\[\begin{cases} x\equiv -a\pmod{b}&(1) \\[2ex] x\equiv -b\pmod a&(2) \\[2ex] x\equiv -1\pmod a&(3) \end{cases} \]

  通过 \((2)(3)\) 发现他们的必要条件\(b=ka+1(k\ge 2)\)(因为我们要使 \(x\)\(x+a+1\) 联通,进而和 \(x+1\) 联通,因此 \(k\) 不能取 \(1\)),于是我们只需要取 \(k=2\),即令 \(b=2a+1\),然后满足 \((1)(3)\) 两个方程即可。

  总结一下,我们需要干的事情:算出 \(\displaystyle L=\prod_{p\in \Bbb P}^{p\le n}p\),找到一个 \(a>\sqrt n\) 使得 \(a,2a+1\) 均为质数,然后找到一个 \(x\) 使得 \(\displaystyle \brak{\prod_{p\in \Bbb P}^{p\neq a\land p\neq b\land p\le n}p}\mid x\land a\mid (x+1)\land (2a+1)\mid (x+a)\),这个 \(x\) 就是我们想要求的答案,至于怎么找到 \(x\),稍微变个形:

\[a\mid(x+1)\land(2a+1)\mid(x+1)\Leftrightarrow a(2a+1)\mid (x+3a+1) \]

  暴力找即可。

  另外,不难证明打表发现\(n>35\) 时,总能找到一个 \(a>\sqrt n\) 使得 \(a,2a+1\) 均为质数且有一个合法的 \(x\). 所以较小范围还得写个暴力判断一下。

  至于题解做法,见鬼去吧。

/** @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

  把带 \(\log\) 的算法几乎全部遍历了一遍,然后不会,分块几乎都是往大小点想,完全没想到对操作序列进行分块......不过确实应当往这个方面想一下的,对于一个点,操作 1 显然只有最后一次才真正有效,就凭这一点,也应当想一想操作分块。

  对于操作 \(1\),可以设置一个阈值 \(\sqrt n\),当这种操作堆到 \(\sqrt n\) 大小的时候再全部同时操作,对于剩下的,可以在询问的时候直接暴力扫,这个部分的复杂度是 \(\mathcal O(n\sqrt n)\).

  对于操作 2,实际上对于一个点 \(x\),真正有用的操作 2 是介于最后一个覆盖它的 1 操作和询问它的 3 操作之间,也就是说,我们需要处理出一个操作区间内,能够到达 \(x\) 的最小值。因此,我们需要处理出 \(minv(id,u)\),即第 \(id\) 个块对于 \(u\) 的最小值是多少(如果没有,就设为 \(+\infty\)),处理询问的时候,边角料就暴力问了。

  现在唯一的问题就是处理出 \(u\) 能否到 \(v\) 这个问题了,直接开 \(10^{10}\)bitset 怕是有点困难,于是乎,把点集每 \(10^4\) 分成一组,处理出每个点到这 \(10^4\) 个点的可达情况,这样空间大概就只需要 \(\mathcal O\brak{\frac{10^9}{\omega}}\approx 120\rm MB\) 了。

  最后的复杂度大概是 \(\mathcal O(10n\sqrt n)\) 吧,效率还行。

/** @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;
}
posted @ 2022-03-14 22:11  Arextre  阅读(57)  评论(0编辑  收藏  举报