[题解]高级监狱

\[\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-02-06 高级监狱

  唯一的好处是部分分特别多,但是一个题都不会......

我醉 / Name

  刚开始想了一个 DS 的做法,但是被 Hack 了,然后就开始打部分分,不得不说,就是因为这个题部分分特别多,让我至少一场考试感觉自己做了点事然而还是一事无成......,直接开正解吧。

  的确是考虑 DS,不过你应当先想到对答案进行二分,在 DS 的时候,从分治中心断开的回文串,必定是 短+长 的形式,这个时候考虑在长链处统计答案,可以求得短链长,这个时候分成两个部分,一是和短链匹配的部分,二是中间剩下的那个部分,我们需要做的是看看有没有短链可以匹配,以及没有和短链匹配的部分是否也是回文的。

  实现的时候注意复杂度,复杂度 \(\mathcal O(n\log^2 n)\) 并且需要较小的常数。

/** @author Arextre */

#include <bits/stdc++.h>
#include <bits/extc++.h>
using namespace std;

#define USING_FREAD
// #define NDEBUG
// #define NCHECK
#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))
#define rqr(x) ((x) * (x))
#define y0 FUCK_UP
#define y1 MOTHER_FUCKER
#ifdef NCHECK
# define iputs(Content) ((void)0)
# define iprintf(Content, argvs...) ((void)0)
#else
# define iputs(Content) fprintf(stderr, Content)
# define iprintf(Content, argvs...) fprintf(stderr, Content, argvs)
#endif

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); }

#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 = 2e5; // twice
const int scale = 1003;

int n;
struct Edge { int u, v, w; } Es[maxn + 5];
inline void input() {
    readin(n);
    rep (i, 1, n - 1) readin(Es[i].u, Es[i].v, Es[i].w);
}

int ch[maxn + 5];
vector<int> g[maxn + 5];
inline void link(int u, int v) {
    g[u].push_back(v), g[v].push_back(u);
}

inline void rebuild() {
    rep (i, 1, n) ch[i] = 1001;
    rep (i, 1, n - 1) {
        ch[n + i] = Es[i].w;
        link(Es[i].u, n + i), link(Es[i].v, n + i);
    }
}

int tail[maxn + 5], ecnt, rt;
struct edge { int to, nxt; } e[maxn << 1 | 3];
inline void add_edge(int u, int v) {
    e[ecnt] = edge{ v, tail[u] }; tail[u] = ecnt++;
    e[ecnt] = edge{ u, tail[v] }; tail[v] = ecnt++;
}

bool vis[maxn + 5];
int mxd[maxn + 5];
namespace DS {

int mx[maxn + 5], siz[maxn + 5];
void findrt(int u, int par, int n, int& rt) {
    siz[u] = 1, mx[u] = 0;
    for (const int& v: g[u]) if (!vis[v] && v != par) {
        findrt(v, u, n, rt), siz[u] += siz[v];
        chkmax(mx[u], siz[v]);
    }
    chkmax(mx[u], n - siz[u]);
    if (mx[u] < mx[rt]) rt = u;
}
void dfs(int u, int par, int d, int& mx) {
    siz[u] = 1; chkmax(mx, d);
    for (const int& v: g[u]) if (!vis[v] && v != par)
        dfs(v, u, d + 1, mx), siz[u] += siz[v];
}
inline int launch(int, int); // claim
void divide(int u) {
    vis[u] = true, mxd[u] = 1;
    for (const int& v: g[u]) if (!vis[v]) {
        dfs(v, u, 2, mxd[u]);
        add_edge(u, launch(v, siz[v]));
    }
}
inline int launch(int u, int n) {
    int rt = 0; mx[rt] = n;
    findrt(u, -1, n, rt);
    return divide(rt), rt;
}

} // namespace DS;

__gnu_pbds::gp_hash_table<ull, bool> T;
int stk[maxn + 5];
ull pow_scale[maxn + 5];
ull dward[maxn + 5], uward[maxn + 5];
bool dfs1(int u, int par, int d, int L) {
    stk[d] = u; // push into the stack
    dward[u] = dward[par] + pow_scale[d] * ch[u];
    uward[u] = uward[par] * scale + ch[u];
    if (d >= L >> 1) { // it can be the longer chain
        int len_of_short = L - d - 1;
        int p = stk[d - len_of_short];
        if (dward[p] == uward[p]) { // optimize: avoid the calculation of ull
            ull tail = uward[u] - uward[p] * pow_scale[len_of_short];
            if (T[tail]) return true;
        }
    }
    for (const int& v: g[u]) if (!vis[v] && v != par)
        if (dfs1(v, u, d + 1, L))
            return true;
    return false;
}
/** @warning @p up should not include the divide root */
void dfs2(int u, int par, ull up, int d, int L) {
    if (d > L >> 1) return; // not qualified
    T[up] = true;
    for (const int& v: g[u]) if (!vis[v] && v != par)
        dfs2(v, u, up * scale + ch[v], d + 1, L);
}
bool dfs(int u, int L) {
    if (mxd[u] < L >> 1) return false; // optimize
    vis[u] = true, dward[u] = uward[u] = ch[u], stk[0] = u;
    T.clear();
    for (const int& v: g[u]) if (!vis[v]) {
        if (dfs1(v, u, 1, L)) return true;
        dfs2(v, u, ch[v], 1, L);
    }
    T.clear();
    reverse(g[u].begin(), g[u].end());
    for (const int& v: g[u]) if (!vis[v]) {
        if (dfs1(v, u, 1, L)) return true;
        dfs2(v, u, ch[v], 1, L);
    }
    for (int i = tail[u], v; ~i; i = e[i].nxt) if (!vis[v = e[i].to])
        if (dfs(v, L)) return true;
    return false;
}
/** @warning @p L should always be an odd number */
inline bool check(int L) {
    memset(vis + 1, false, n << 1);
    return dfs(rt, L);
}

signed main() {
    freopen("name.in", "r", stdin);
    freopen("name.out", "w", stdout);
    input(); rebuild();
    memset(tail, 0xff, sizeof tail);
    rt = DS::launch(1, (n << 1) - 1);
    pow_scale[0] = 1;
    rep (i, 1, maxn) pow_scale[i] = pow_scale[i - 1] * scale;
    int ans = -1;
    for (int l = 1, r = n, mid; (mid = l + r >> 1, true) && l <= r; ) {
        if (check(mid << 1 | 1)) ans = mid << 1 | 1, l = mid + 1;
        else r = mid - 1;
    }
    writln(ans >> 1);
    return 0;
}

梧桐依旧 / Tree

  他妈的线代题,是个人都不会往群论方向想这个问题看到这个题,我想到的突破口是先解决判定问题:快速地判定 \(A\equiv BA\pmod p\),这个时候我类比于 神奇的矩阵 这个题,那么我就先随机一个向量 \(\Vec v\) 出来,然后,我们只需要判定 \(A\Vec v\equiv BA\Vec v\pmod p\),然后我就走不下去了......

  其实这个题正确的方向是群论......将 \(B\) 视作一个置换,那么 \(A\equiv BA\pmod p\) 实际上就是在说 \(A\) 是 \(B\) 在模 \(p\) 下的不动点,而 \(B\) 满秩的意图是所有的 \(B\) 构成一个置换群......那么我们考虑 Burnside 引理:

\[|X/G|=\frac{1}{|G|}\sum_{g\in G}|X^g| \]

  \(X/G\) 表示在置换群 \(G\) 下的等价类集合,\(X^g=\set{x|x\in X\land g(x)=x}\),即 \(g\) 作用在 \(X\) 中的不动点数。而我们想要求的就是 \(\displaystyle \sum_{g\in G}|X^g|\),尝试能否算出 \(|G|\cdot |X/G|\),这样我们就变着法子求出了答案。下记 \(k=p\),虽然不知道有什么用,但是题解也这样写,我也适应了......方便行文,也就这样了。

  先解决 \(|G|\),这个东西就是 \(n\) 阶满秩矩阵的数量,将矩阵看做 \(n\) 个行向量构成的行向量组,一个一个向量往里面填。考虑第一个向量,除了 \(\Vec 0\) 以外它都可以选,有 \(k^n-1\) 中,由于第一个向量可以乘上 \(0\sim k-1\) 中的任意一个构成线性相关的另一个向量,因此第二个向量有 \(k^n-k\) 种......不难发现,存在:

\[|G|=\prod_{j=0}^{n-1}(k^n-k^j) \]

  现在考虑求解 \(|X/G|\),也就是在 \(G\) 置换群下的轨道数量。显然,\(A\) 与 \(A'\) 位于同一轨道(同构)当且仅当 \(A\) 向量组中的所有向量可以被 \(A'\) 向量组线性组合出,并且 \(G\) 中的矩阵都是满秩,不存在坍缩的情况,即 \(\rank(BA)=\rank(A)\),那么 \(\rank\) 不同的矩阵肯定不同构,因此我们可以分不同 \(\rank\) 求,再加起来得到 \(|X/G|\).

  假设当前我们所求 \(\rank(A)=i\),考虑秩为 \(i\) 且不同构的矩阵数量,显然交换行列不影响同构,并且将任意向量数乘也不影响同构,那么我们可以假定前 \(i\) 行向量线性无关,仿照求 \(|G|\),不难得出方案数为 \(\displaystyle \prod_{j=0}^{i-1}(k^n-k^j)\). 接下来我们还要考虑有多少 \(A\) 在 \(G\) 作用下是位于同一轨道上的,实际上就是线性无关的,且秩为 \(i\) 的矩阵的数量,可以直接套用 \(|G|\) 的结论,得到一个轨道的长度为 \(\prod_{j=0}^{i-1}(k^i-k^j)\),也就是一个矩阵被算了多少次,除掉就好了。

 最后把上述部分组合起来就好了,最后的答案就是

\[\brak{\prod_{j=0}^{n-1}k^n-k^j}\sum_{i=0}^{n}\prod_{j=0}^{i-1}\frac{k^n-k^j}{k^i-k^j} \]

  用一点技巧,复杂度为 \(\mathcal O(n)\).

/** @author Arextre */

#include <bits/stdc++.h>
using namespace std;

#define USING_FREAD
// #define NDEBUG
// #define NCHECK
#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))
#define rqr(x) ((x) * (x))
#define y0 FUCK_UP
#define y1 MOTHER_FUCKER
#ifdef NCHECK
# define iputs(Content) ((void)0)
# define iprintf(Content, argvs...) ((void)0)
#else
# define iputs(Content) fprintf(stderr, Content)
# define iprintf(Content, argvs...) fprintf(stderr, Content, argvs)
#endif

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); }

#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 mod = 998244353;
const int maxn = 30000000;
inline void chkadd(int& x, const int y) {
    if ((x += y) >= mod) x -= mod;
}

inline int qkpow(int a, int n) {
    int ret = 1;
    for (; n; n >>= 1, a = 1ll * a * a % mod)
        if (n & 1) ret = 1ll * ret * a % mod;
    return ret;
}

int n, k;
int powk[maxn + 5];
int invk[maxn + 5];

signed main() {
    freopen("tree.in", "r", stdin);
    freopen("tree.out", "w", stdout);
    readin(n, k);
    powk[0] = invk[0] = 1;
    rep (i, 1, n) {
        powk[i] = 1ll * powk[i - 1] * k % mod;
        invk[i] = 1ll * invk[i - 1] * (powk[i] + mod - 1) % mod;
    }
    invk[n] = qkpow(invk[n], mod - 2);
    drep (i, n - 1, 1)
        invk[i] = 1ll * invk[i + 1] * (powk[i + 1] + mod - 1) % mod;
    int coe = 1;
    rep (i, 0, n - 1) coe = 1ll * coe * (powk[n] + mod - powk[i]) % mod;
    int sum = 1, ik = qkpow(k, mod - 2), powik = 1, mom = 1;
    rep (i, 1, n) {
        mom = 1ll * (powk[n] + mod - powk[i - 1]) * mom % mod * powik % mod;
        chkadd(sum, 1ll * mom * invk[i] % mod);
        powik = 1ll * powik * ik % mod;
    }
    writln(1ll * sum * coe % mod);
    return 0;
}

卿且去 / Yyds

  结论很朴素,想到它需要灵感,证明它倒很朴素,至于我是怎么想到这个结论的呢?这就很有意思了。

  设选出的质数集为 \(\mathscr P\),在选出的质数中有一类特别特殊 —— \(p\in \mathscr P\land p>\ddiv{N}{2}\),这样的质数我们一定会选,相当于它是一个孤点。然后我就考虑扩展这个结论 —— 选择集合中超过 \(\ddiv{N}{2}\) 的数,显然选完之后就没得其他还可以加入集合的数了。

  想到这个结论之后,证明就特别平凡了 —— 调整法。设在 \(\mathscr P\) 基础下生成的集合 \(\mathscr A\) 中,选出最大的满足条件的集合为 \(\mathscr S\),如果上述结论不成立,那么 \(\exists y\in \mathscr S,y\le \ddiv{N}{2}\),那么,我们选择最小的满足该条件的数 \(x\),尝试将 \(x\to 2x\),显然 \(2x\in \mathscr A\),如果无法选择 \(2x\),则说明 \(\exists z\in S\land z\neq x,z\mid (2x)\),由于 \(z\nmid x\),那么 \(2\mid z\),且 \(\frac{z}{2}\mid x\),并且 \(z>x\Leftrightarrow \frac{z}{2}>\frac{x}{2}\),所以唯一解即 \(z=2x\),与 \(z\nmid x\) 矛盾。

  那么答案就很好算了:\(\displaystyle \sum_{i=\ddiv{N}{2}+1}^N2^{\pi(N)-d(i)}\times (2^{d(i)}-1)=\brak{N-\ddiv{N}{2}}2^{\pi(N)}-2^{\pi(N)}\sum_{i=\ddiv{N}{2}+1}^N2^{-d(i)}\),而 \(\pi(N)\) 与后面那个求和都是可以直接 Min25 筛出来的,所以直接筛,复杂度 \(\mathcal O\brak{\frac{N^{\frac{3}{4}}}{\ln N}}\).

/** @author Arextre */

#include <bits/stdc++.h>
using namespace std;

#define USING_FREAD
// #define NDEBUG
// #define NCHECK
#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))
#define rqr(x) ((x) * (x))
#define y0 FUCK_UP
#define y1 MOTHER_FUCKER
#ifdef NCHECK
# define iputs(Content) ((void)0)
# define iprintf(Content, argvs...) ((void)0)
#else
# define iputs(Content) fprintf(stderr, Content)
# define iprintf(Content, argvs...) fprintf(stderr, Content, argvs)
#endif

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); }

#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 mod = 998244353;
const int inv2 = 499122177;
const int maxn = 1e5 * 2;

inline int qkpow(int a, int n) {
    int ret = 1;
    for (; n; n >>= 1, a = 1ll * a * a % mod)
        if (n & 1) ret = 1ll * ret * a % mod;
    return ret;
}

int prime[maxn + 5];
bool vis[maxn + 5];
inline void sieve(int n) {
    vis[1] = true;
    for (int i = 2; i <= n; ++i) {
        if (!vis[i]) prime[++*prime] = i;
        for (int j = 1; j <= *prime && i * prime[j] <= n; ++j) {
            vis[i * prime[j]] = true;
            if (i % prime[j] == 0) break;
        }
    }
}

ll n, sqn;
int id[2][maxn + 5], wcnt;
ll w[maxn + 5];
inline void prelude() {
    sqn = static_cast<ll>(sqrt(n));
    for (ll l = 1, r; l <= n; l = r + 1) {
        r = n / (n / l), w[++wcnt] = n / l;
        if (w[wcnt] <= sqn) id[0][w[wcnt]] = wcnt;
        else id[1][n / w[wcnt]] = wcnt;
    }
}
inline int getid(ll x) {
    if (x <= sqn) return id[0][x];
    return id[1][n / x];
}

int g[maxn + 5], pin;
inline void getG() {
    rep (i, 1, wcnt) g[i] = (w[i] - 1) % mod; // except for f(1)
    rep (j, 1, *prime) {
        for (int i = 1; i <= wcnt && 1ll * prime[j] * prime[j] <= w[i]; ++i) {
            g[i] = (g[i] + mod - g[getid(w[i] / prime[j])] + (j - 1)) % mod;
        }
    }
    pin = g[getid(n)];
    rep (i, 1, wcnt) g[i] = 1ll * inv2 * g[i] % mod;
}

inline int S(ll n, int i) {
    if (n < 2 || (i? prime[i]: 0) >= n) return 0;
    int ret = (g[getid(n)] + mod - 1ll * i * inv2 % mod) % mod;
    for (int j = i + 1; j <= *prime && 1ll * prime[j] * prime[j] <= n; ++j) {
        for (ll k = 1, p = prime[j]; p <= n; p *= prime[j], ++k) {
            ret = (ret + 1ll * inv2 * (S(n / p, j) + (k > 1? 1: 0))) % mod;
        }
    }
    return ret;
}

signed main() {
    freopen("yyds.in", "r", stdin);
    freopen("yyds.out", "w", stdout);
    readin(n);
    prelude();
    sieve(sqn);
    getG();
    int ans1 = S(n, 0), ans2;
    ans1 = (ans1 + mod - S(n >> 1, 0)) % mod;
    ans2 = 1ll * (n - (n >> 1)) % mod * qkpow(2, pin) % mod;
    ans2 = (ans2 + mod - 1ll * qkpow(2, pin) * ans1 % mod) % mod;
    writln(ans2);
    return 0;
}
posted @ 2022-03-14 21:49  Arextre  阅读(299)  评论(0)    收藏  举报