重庆八中集训 day1
T1
题意
给你一个长度为 \(n\) 的序列 \(a\),\(Q\) 次查询,每次查询 \([l,r]\) 区间内把所有出现次数大于 \(w\) 的数字视为 \(\infty\) 之后的第 \(k\) 小值。
满足 \(0\le a_i < N, 1\le N, Q\le 10 ^ 5\)
题解
忽视次数为 \(w\) 的数字,怎么办?思考一下,发现可以搞一个暴力莫队+线段树,时间复杂度为 \(O(N\log N \sqrt N)\)。发现只有 \(N\) 次询问但是居然有 \(N\sqrt N\) 次插入和删除,我们如果使用线段树很亏,考虑平衡一下复杂度,使用插入 \(O(1)\),查询 \(O(\sqrt N)\) 的分块来维护即可。具体而言,一个区间k大是有方法 \(O(1)-O(\sqrt N)\) 的,具体做法是维护值域分块,还有一个桶。
值域分块就是把值域做分块,维护每一块有多少个数。那么可以用 \(\sqrt N\) 的时间确定答案在哪一块,然后暴力扫这个块,通过桶定位答案。
现在它要求我们在线,思考能不能去掉莫队。那么问题就是如何在线得到一段区间的值域分块?
怎么办?有一个解决方法是把序列再次分块,预处理第 \(i\) 块到第 \(j\) 块的值域分块,然后复杂度都是 \(O(N\sqrt N)\) 的。桶怎么做?我们只要看看能不能快速得到一个数在区间内的出现次数即可。
怎么办?考虑维护 \(num[i][j]\) 表示数字 \(i\) 在 \(1\sim j\) 块内出现了多少次,那么整块直接做差,然后边角可以暴力加入桶就好了。
// Siriqwq
#include <bits/stdc++.h>
using std::cin;
using std::cout;
using std::vector;
using std::copy;
using std::reverse;
using std::sort;
using std::get;
using std::unique;
using std::swap;
using std::array;
using std::cerr;
using std::function;
using std::map;
using std::set;
using std::pair;
using std::mt19937;
using std::make_pair;
using std::tuple;
using std::make_tuple;
using std::uniform_int_distribution;
using ll = long long;
namespace qwq {
mt19937 eng;
void init(int Seed) {return eng.seed(Seed);}
int rnd(int l = 1, int r = 1000000000) {return uniform_int_distribution<int> (l, r)(eng);}
}
template<typename T>
inline void chkmin(T &x, T y) {if (x > y) x = y;}
template<typename T>
inline void chkmax(T &x, T y) {if (x < y) x = y;}
template<typename T>
inline T min(const T &x, const T &y) {return x < y ? x : y;}
template<typename T>
inline T max(const T &x, const T &y) {return x > y ? x : y;}
char buf[100000], *bufs, *buft;
#define gc() ((bufs == buft && (buft = (bufs = buf) + fread(buf, 1, 100000, stdin))), bufs == buft ? -1 : *bufs++)
template<typename T>
inline void read(T &x) {
x = 0;
bool f = 0;
char ch = gc();
while (!isdigit(ch)) f = ch == '-', ch = gc();
while (isdigit(ch)) x = x * 10 + ch - '0', ch = gc();
if (f) x = -x;
}
inline void reads(char *s) {
char ch = gc();
while (isspace(ch)) ch = gc();
while (!isspace(ch) && ch != EOF) *(s++) = ch, ch = gc();
*s = 0;
return;
}
template<typename T, typename ...Arg>
inline void read(T &x, Arg &... y) {
read(x);
read(y...);
}
#define O(x) cerr << #x << " : " << x << '\n'
const double Pi = acos(-1);
const int MOD = 998244353, inv2 = (MOD + 1) / 2, I32_INF = 0x3f3f3f3f;
const long long I64_INF = 0x3f3f3f3f3f3f3f3f;
auto Ksm = [] (int x, int y) -> int {
if (y < 0) {
y %= MOD - 1;
y += MOD - 1;
}
int ret = 1;
for (; y; y /= 2, x = (long long) x * x % MOD) if (y & 1) ret = (long long) ret * x % MOD;
return ret;
};
auto Mod = [] (int x) -> int {
if (x >= MOD) return x - MOD;
else if (x < 0) return x + MOD;
else return x;
};
template<const int N_num, const int M_num>
struct Graph {
int H[N_num];
struct Edge {int to, lac;} e[M_num];
inline void add_edge(int x, int y) {e[*H] = {y, H[x]};H[x] = (*H)++;}
inline void init() {memset(H, -1, sizeof H);*H = 0;}
};
#define go(x, y) for (int i = x.H[y], v; (v = x.e[i].to) && ~i; i = x.e[i].lac)
inline int ls(int k) {return k << 1;}
inline int rs(int k) {return k << 1 | 1;}
using ull = unsigned long long;
const int MAXN = 1e5 + 10, MAXB = 505;
int buc[MAXN], F[505][505][505], num[MAXN][505];
// buc[i] 表示桶, F[i][j][k] 表示第 i 块到第 j 块的值域分块中第 k 块的总和
// num[i][j] 表示数字 i 在前 j 块的出现次数
int N, w, type, Q, blk_sz, bel[MAXN], L[MAXB], R[MAXB], blk_num, A[MAXN];
int tmp[MAXB];
inline int NUM(int l, int r, int p) {return num[p][r] - num[p][l - 1];}
int qry(int ql, int qr, int K) {
memset(tmp, 0, sizeof tmp);
int ans = N + 1;
if (bel[ql] == bel[qr]) {
for (int i = ql; i <= qr; ++i) {
if (++buc[A[i]] == w + 1) {
tmp[bel[A[i]]] -= w;
} else if (buc[A[i]] <= w) ++tmp[bel[A[i]]];
}
int p = 0;
for (int i = 1; i <= blk_num && K > 0; ++i) {
if (K <= tmp[i]) p = i;
K -= tmp[i];
}
K += tmp[p];
if (p) for (int i = L[p]; i <= R[p] && K > 0; ++i) if (buc[i] <= w) {
if (K <= buc[i]) ans = i;
K -= buc[i];
}
for (int i = ql; i <= qr; ++i) --buc[A[i]];
} else {
int bl = bel[ql], br = bel[qr];
memcpy(tmp, F[bl + 1][br - 1], sizeof tmp);
for (int i = ql; i <= R[bl]; ++i) {
if (++buc[A[i]] + NUM(bl + 1, br - 1, A[i]) == w + 1) tmp[bel[A[i]]] -= w;
else if (buc[A[i]] + NUM(bl + 1, br - 1, A[i]) <= w) ++tmp[bel[A[i]]];
}
for (int i = L[br]; i <= qr; ++i) {
if (++buc[A[i]] + NUM(bl + 1, br - 1, A[i]) == w + 1) tmp[bel[A[i]]] -= w;
else if (buc[A[i]] + NUM(bl + 1, br - 1, A[i]) <= w) ++tmp[bel[A[i]]];
}
int p = 0;
for (int i = 1; i <= blk_num && K > 0; ++i) {
if (K <= tmp[i]) p = i;
K -= tmp[i];
}
K += tmp[p];
if (p) for (int i = L[p]; i <= R[p] && K > 0; ++i) if (buc[i] + NUM(bl + 1, br - 1, i) <= w) {
if (K <= buc[i] + NUM(bl + 1, br - 1, i)) ans = i;
K -= buc[i] + NUM(bl + 1, br - 1, i);
}
for (int i = ql; i <= R[bl]; ++i) --buc[A[i]];
for (int i = L[br]; i <= qr; ++i) --buc[A[i]];
}
return ans;
}
int main() {
freopen("kth.in", "r", stdin);
freopen("kth.out", "w", stdout);
// std::ios::sync_with_stdio(0);
// cout << std::fixed << std::setprecision(8);
// cin.tie(0);
// cout.tie(0);
qwq::init(20050112);
read(N, w, Q, type);
blk_sz = 200;
for (int i = 1; i <= N; ++i) {
R[bel[i] = (i - 1) / blk_sz + 1] = i;
if (!L[bel[i]]) L[bel[i]] = i;
}
blk_num = bel[N];
for (int i = 1; i <= N; ++i) read(A[i]), ++A[i];
for (int i = 1; i <= blk_num; ++i) {
for (int j = i; j <= blk_num; ++j) {
memcpy(F[i][j], F[i][j - 1], sizeof F[i][j]);
for (int k = L[j]; k <= R[j]; ++k) {
if (++buc[A[k]] == w + 1) F[i][j][bel[A[k]]] -= w;
else if (buc[A[k]] <= w) ++F[i][j][bel[A[k]]];
}
if (i == 1) for (int k = 1; k <= N; ++k) num[k][j] = buc[k];
}
memset(buc, 0, sizeof buc);
}
for (int lstans = 0, l, r, k; Q--; ) {
read(l, r, k);
l ^= lstans * type;
r ^= lstans * type;
k ^= lstans * type;
lstans = qry(l, r, k);
printf("%d\n", --lstans);
}
// cout << (-3 / 2);
cerr << ((double) clock() / CLOCKS_PER_SEC) << '\n';
return (0-0);
}
T2
题意
众所周知 $\mu(i) = \prod_{i}(-1) ^ {a_i}[a_i \le 1] $,记 \(f_d(x) =\prod_{i}(-1) ^ {a_i}[a_i \le d]\)
求 \(\sum_{x = 1} ^ {N}\sum_{y = 1} ^ N\sum_{d = 1} ^ K f_d((x, y))\)
题解
我们 \(\varphi (x)\) 的前缀和可以使用杜教筛解决,那么现在的问题就是对于某个 \(d\),怎么求 \(\sum_{i = 1} ^ x f_d(i)\)
记 \(x\) 分解的结果是 \(\prod_{i} p_i ^ {a_i}\),定义 \(\lambda(x) = f_{\infty}(x)\),\(\lambda\) 是完全积性函数。
我们考虑 \(i ^ {d + 1} | x\) 的话,那么 \(f_{d}(x)= 0\),按照 \(\sum_{i|x} \mu(i) = [x = 1]\),记 \(F_d (x) = \sum_{i = 1} ^ xf_d(i)\)可以得到
记 \(\Lambda (x) = \sum_{i = 1} ^ x \lambda(i)\),应该有 \(F_d(x) = \sum_{i = 1} ^ x \lambda ^ {d + 1}(i) \Lambda(\left\lfloor\frac{N}{i ^ {d + 1}} \right\rfloor)\),更进一步的,应该是 \(F_d(x) = \sum_{i = 1} ^ {\left \lfloor x ^ \frac{1}{d + 1} \right\rfloor} \lambda ^ {d + 1}(i) \mu(i) \Lambda (\left \lfloor\frac{N}{i ^ {d + 1}}\right\rfloor)\)。
\(x\) 较小的时候预处理,\(x\) 较大的时候直接计算,如果 \(\Lambda (x)\) 可以 \(O(1)\) 算,那就可以直接搞。
一个很牛的性质:\(\sum_{d | x} \lambda(x) = [x = y ^ 2]\),所以可以杜教筛,它可以卷上 \(I\) 然后就可以杜教筛。
所以我们就可以 \(O(N ^ \frac{2}{3})\) 的解决这个问题。
ps:如果不会杜教筛(我就不会),接着往下看,如果我要求
这歌东西的前缀和,那么我们考虑找一个积性函数 \(g\),如果能快速计算什么迪利克雷前缀和函数的东西。
然后就可以做了,你考虑 \(\varphi * I = id\) 即可。
// Siriqwq
#include <bits/stdc++.h>
using std::cin;
using std::cout;
using std::vector;
using std::copy;
using std::reverse;
using std::sort;
using std::get;
using std::unique;
using std::swap;
using std::array;
using std::cerr;
using std::function;
using std::map;
using std::set;
using std::pair;
using std::mt19937;
using std::make_pair;
using std::tuple;
using std::make_tuple;
using std::uniform_int_distribution;
using ll = long long;
namespace qwq {
mt19937 eng;
void init(int Seed) {return eng.seed(Seed);}
int rnd(int l = 1, int r = 1000000000) {return uniform_int_distribution<int> (l, r)(eng);}
}
template<typename T>
inline void chkmin(T &x, T y) {if (x > y) x = y;}
template<typename T>
inline void chkmax(T &x, T y) {if (x < y) x = y;}
template<typename T>
inline T min(const T &x, const T &y) {return x < y ? x : y;}
template<typename T>
inline T max(const T &x, const T &y) {return x > y ? x : y;}
char buf[100000], *bufs, *buft;
#define gc() ((bufs == buft && (buft = (bufs = buf) + fread(buf, 1, 100000, stdin))), bufs == buft ? -1 : *bufs++)
template<typename T>
inline void read(T &x) {
x = 0;
bool f = 0;
char ch = gc();
while (!isdigit(ch)) f = ch == '-', ch = gc();
while (isdigit(ch)) x = x * 10 + ch - '0', ch = gc();
if (f) x = -x;
}
inline void reads(char *s) {
char ch = gc();
while (isspace(ch)) ch = gc();
while (!isspace(ch) && ch != EOF) *(s++) = ch, ch = gc();
*s = 0;
return;
}
template<typename T, typename ...Arg>
inline void read(T &x, Arg &... y) {
read(x);
read(y...);
}
#define O(x) cerr << #x << " : " << x << '\n'
const double Pi = acos(-1);
const int MOD = (1 << 30) - 1, I32_INF = 0x3f3f3f3f;
const long long I64_INF = 0x3f3f3f3f3f3f3f3f;
auto Ksm = [] (int x, int y) -> int {
if (y < 0) {
y %= MOD - 1;
y += MOD - 1;
}
int ret = 1;
for (; y; y /= 2, x = (long long) x * x % MOD) if (y & 1) ret = (long long) ret * x % MOD;
return ret;
};
template<const int N_num, const int M_num>
struct Graph {
int H[N_num];
struct Edge {int to, lac;} e[M_num];
inline void add_edge(int x, int y) {e[*H] = {y, H[x]};H[x] = (*H)++;}
inline void init() {memset(H, -1, sizeof H);*H = 0;}
};
#define go(x, y) for (int i = x.H[y], v; (v = x.e[i].to) && ~i; i = x.e[i].lac)
inline int ls(int k) {return k << 1;}
inline int rs(int k) {return k << 1 | 1;}
using ull = unsigned long long;
const int MAXV = 5e6 + 10, LIM = 5e6, LIMM = 1e6;
using ui = unsigned int;
ll N;
ui K, vis[MAXV], pri[MAXV], phi[MAXV], sphi[MAXV], lambda[MAXV], slambda[MAXV], mu[MAXV], ans;
ui f[41][LIMM + 10];
void sieve() {
for (int i = 1; i <= K; ++i) f[i][1] = 1;
for (ui i = 2; i < MAXV; ++i) {
if (!vis[i]) {
pri[++*pri] = i, phi[i] = i - 1, lambda[i] = -1, mu[i] = -1;
if (i <= LIMM) for (int j = 1; j <= K; ++j) f[j][i] = -1;
}
for (ui j = 1; j <= *pri && pri[j] * i < MAXV; ++j) {
vis[i * pri[j]] = 1;
lambda[i * pri[j]] = -lambda[i];
if (i % pri[j]) {
phi[i * pri[j]] = phi[i] * phi[pri[j]];
mu[i * pri[j]] = -mu[i];
int x = i * pri[j];
if (x <= LIMM) for (int t = 1; t <= K; ++t) f[t][x] = -f[t][i];
}
else {
phi[i * pri[j]] = phi[i] * pri[j];
int tot = 1, tmp = i, x = pri[j] * i;
if (x <= LIMM) {
while (tmp % pri[j] == 0) ++tot, tmp /= pri[j];
for (int t = tot; t <= K; ++t) f[t][x] = -f[t][i];
}
break;
}
}
}
sphi[1] = phi[1] = 1;
mu[1] = 1;
slambda[1] = lambda[1] = 1;
for (int i = 2; i <= LIM; ++i) {
sphi[i] = sphi[i - 1] + phi[i], slambda[i] = slambda[i - 1] + lambda[i];
if (i <= LIMM) for (int j = 1; j <= K; ++j) f[j][i] += f[j][i - 1];
}
}
namespace get_phi {
std::unordered_map<ll, ui> mp;
ui getphi(ll x) {
if (x <= LIM) return sphi[x];
if (mp.count(x)) return mp[x];
ui y = (ui) x, ret = y * (y + 1) / 2;
for (ll l = 2, r; l <= x; l = r + 1) {
r = x / (x / l);
ret -= (r - l + 1U) * getphi(x / l);
}
return mp[x] = ret;
}
}
namespace get_lambda {
std::unordered_map<ll, ui> mp;
ui getlambda(ll x) {
if (x <= LIM) return slambda[x];
if (mp.count(x)) return mp[x];
ui y = (ui) x, ret = sqrt(x);
for (ll l = 2, r; l <= x; l = r + 1) {
r = x / (x / l);
ret -= (r - l + 1U) * getlambda(x / l);
}
return mp[x] = ret;
}
}
ll sm[100010][41];
inline ui F(int d, ll x) {
if (x <= LIMM) return f[d][x];
ll lim = pow(x, 1.0 / (d + 1)) + 1;
ui ret = 0;
for (ll l = 1; l <= lim; ++l) if (mu[l]) {
ui k = get_lambda::getlambda(x / sm[l][d]) * mu[l];
if (lambda[l] == 1) ret += k;
else if (d & 1) ret += k;
else ret -= k;
}
return ret;
}
int main() {
freopen("sum.in", "r", stdin);
freopen("sum.out", "w", stdout);
// std::ios::sync_with_stdio(0);
// cout << std::fixed << std::setprecision(8);
// cin.tie(0);
// cout.tie(0);
qwq::init(20050112);
read(N, K);
sieve();
get_lambda::getlambda(N);
get_phi::getphi(N);
for (int i = 1; i < 100010; ++i) {
sm[i][0] = i;
for (int j = 1; j <= 40; ++j) sm[i][j] = sm[i][j - 1] * i;
}
ui ans = 0;
for (ll l = 1, r; l <= N; l = r + 1) {
r = N / (N / l);
ui ret = 0;
for (int i = 1; i <= K; ++i) ret += F(i, r) - F(i, l - 1);
ret *= 2 * get_phi::getphi(N / l) - 1;
ans += ret;
}
printf("%u\n", ans & MOD);
// cout << (-3 / 2);
cerr << ((double) clock() / CLOCKS_PER_SEC) << '\n';
return (0-0);
}
T3
题意
一棵有 \(N\) 个点的树,然后定义一次消息传递为 \(u\to v\),经过的所有有向边 \((x,y)\),如果 \(x<y\) 那么 \(val_x\) 和 \(val_y\) 加1,否则会让 \(val_x\) 和 \(val_y\) 减去1。
然后经过若干次消息传递之后,现在有一个局面,问你原来的消息传递可能是什么。要求最少的消息,并且消息的字典序最小。
\(N\le 10^6\) 并且消息传递次数小于等于 \(N\)。
题解
首先熊子豪跟我说,这题拿来就是能看出来先求出来每条边给两边的点有多少贡献。然后我们可以根据这条边两边的点的大小确定这个边的走向。
之后就是一个树形dp,具体而言我们已经知道了每条边怎么走了,然后要求一个点被当过多少次终点/起点。
一个性质是 \(u\to w, w\to v\) 的路径可以变为 \(u\to v\)。
所以,可以直接树形dp来做,\(ct_i\) 就是一个点被当作起点/终点的次数。
然后我们还有一个性质,对于两条路径 \(a\to d, b\to c\),来说,他和 \(a\to c, b\to d\) 是等效的。
然后直接贪心做就可以。
// Siriqwq
#include <bits/stdc++.h>
using std::cin;
using std::cout;
using std::vector;
using std::copy;
using std::reverse;
using std::sort;
using std::get;
using std::unique;
using std::swap;
using std::array;
using std::cerr;
using std::function;
using std::map;
using std::set;
using std::pair;
using std::mt19937;
using std::make_pair;
using std::tuple;
using std::make_tuple;
using std::uniform_int_distribution;
using ll = long long;
namespace qwq {
mt19937 eng;
void init(int Seed) {return eng.seed(Seed);}
int rnd(int l = 1, int r = 1000000000) {return uniform_int_distribution<int> (l, r)(eng);}
}
template<typename T>
inline void chkmin(T &x, T y) {if (x > y) x = y;}
template<typename T>
inline void chkmax(T &x, T y) {if (x < y) x = y;}
template<typename T>
inline T min(const T &x, const T &y) {return x < y ? x : y;}
template<typename T>
inline T max(const T &x, const T &y) {return x > y ? x : y;}
char buf[100000], *bufs, *buft;
#define gc() ((bufs == buft && (buft = (bufs = buf) + fread(buf, 1, 100000, stdin))), bufs == buft ? -1 : *bufs++)
template<typename T>
inline void read(T &x) {
x = 0;
bool f = 0;
char ch = gc();
while (!isdigit(ch)) f = ch == '-', ch = gc();
while (isdigit(ch)) x = x * 10 + ch - '0', ch = gc();
if (f) x = -x;
}
inline void reads(char *s) {
char ch = gc();
while (isspace(ch)) ch = gc();
while (!isspace(ch) && ch != EOF) *(s++) = ch, ch = gc();
*s = 0;
return;
}
template<typename T, typename ...Arg>
inline void read(T &x, Arg &... y) {
read(x);
read(y...);
}
#define O(x) cerr << #x << " : " << x << '\n'
const double Pi = acos(-1);
const int MAXN = 1048576, MOD = 998244353, inv2 = (MOD + 1) / 2, I32_INF = 0x3f3f3f3f;
const long long I64_INF = 0x3f3f3f3f3f3f3f3f;
auto Ksm = [] (int x, int y) -> int {
if (y < 0) {
y %= MOD - 1;
y += MOD - 1;
}
int ret = 1;
for (; y; y /= 2, x = (long long) x * x % MOD) if (y & 1) ret = (long long) ret * x % MOD;
return ret;
};
auto Mod = [] (int x) -> int {
if (x >= MOD) return x - MOD;
else if (x < 0) return x + MOD;
else return x;
};
template<const int N_num, const int M_num>
struct Graph {
int H[N_num];
struct Edge {int to, lac;} e[M_num];
inline void add_edge(int x, int y) {e[*H] = {y, H[x]};H[x] = (*H)++;}
inline void init() {memset(H, -1, sizeof H);*H = 0;}
};
#define go(x, y) for (int i = x.H[y], v; (v = x.e[i].to) && ~i; i = x.e[i].lac)
inline int ls(int k) {return k << 1;}
inline int rs(int k) {return k << 1 | 1;}
using ull = unsigned long long;
int N, v[MAXN], dp[MAXN], ct[MAXN], fa[MAXN];
Graph<MAXN, MAXN * 2> tr;
void dfs(int u) {
dp[u] = v[u];
go(tr, u) if (v != fa[u]) {
fa[v] = u;
dfs(v);
dp[u] -= dp[v];
}
}
void dfs1(int u) {
ct[u] = dp[u];
go(tr, u) if (v != fa[u]) {
dfs1(v);
ct[u] -= dp[v];
}
}
int main() {
freopen("tree.in", "r", stdin);
freopen("tree.out", "w", stdout);
// std::ios::sync_with_stdio(0);
// cout << std::fixed << std::setprecision(8);
// cin.tie(0);
// cout.tie(0);
qwq::init(20050112);
read(N);
tr.init();
for (int i = 1; i <= N; ++i) read(v[i]);
for (int i = 1, x, y; i < N; ++i) {
read(x, y);
tr.add_edge(x, y);
tr.add_edge(y, x);
}
// O(1);
dfs(1);
for (int i = 2; i <= N; ++i) if (fa[i] > i) dp[i] = -dp[i];
dfs1(1);
vector<int> s, t;
// O(1);
for (int i = 1; i <= N; ++i) if (ct[i] > 0) while (ct[i]--) s.push_back(i);
else if (ct[i] < 0) while (ct[i]++) t.push_back(i);
printf("%u\n", s.size());
for (int i = 0; i < s.size(); ++i) printf("%d %d\n", t[i], s[i]);
// cout << (-3 / 2);
cerr << ((double) clock() / CLOCKS_PER_SEC) << '\n';
return (0-0);
}