[题解]高级监狱

\[\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  阅读(141)  评论(0编辑  收藏  举报