[题解]高级监狱
\(\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\) 表示在置换群 \(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\) 种......不难发现,存在:
现在考虑求解 \(|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)\),也就是一个矩阵被算了多少次,除掉就好了。
最后把上述部分组合起来就好了,最后的答案就是
用一点技巧,复杂度为 \(\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;
}