没交

$\text{score:NULL rk:NULL}$

Problem A:区间第k小

题面

题解

先考虑离线,整体二分,二分答案 $mid$,处理所有 $\le mid$ 的数,那么此时问题就转化为在区间出现次数不超过 $w$ 的数有多少个

不妨关注每个数对询问区间 $[L, R]$ 的贡献,对某个数 $a$,若它是从左往右后 $w$ 个出现为 $a$ 的数,则它的贡献为 $1$,若为倒数第 $w + 1$ 个则贡献为 $- w$,剩下的贡献都为 $0$,那么我们就可以开一个队列,首先总大小(即不删去超过 $w$ 的)算出来,此时计算需要删去的,队列大小未到 $w$ 时没有需要删的,若大于 $w$ 时每次就让队首点贡献减去 $w + 1$,当然之前多减的还要加回来

那么现在就可以考虑从离线转成在线,在整体二分的过程中每一层(即每一次二分的 $[l, r]$)都会建一些可持久化线段树,而它又最多会修改 $O (n \log n)$ 次,开 $O (n \log^2 n)$ 个节点,所以把每一层的可持久化线段树都存下来,在线二分时即可直接查找,总时间复杂度 $O (n \log^2 n)$

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
#include <iostream>
#include <cstdio>
#include <cstring>
#include <vector>
#include <queue>

#define lp p << 1
#define rp p << 1 | 1

using namespace std;

const int MAXN = 1e05 + 10;
const int MAXL = 17;
const int MAXM = MAXN * MAXL * MAXL;

int N, W, Q, type;
int a[MAXN];

int lson[MAXM]= {0}, rson[MAXM]= {0};
int subsum[MAXM]= {0}, nd = 0;
void modify (int pre, int& p, int left, int right, int posi, int x) {
lson[p = ++ nd] = lson[pre], rson[p] = rson[pre];
subsum[p] = subsum[pre] + x;
if (left == right) return ;
int mid = (left + right) >> 1;
if (posi <= mid) modify (lson[pre], lson[p], left, mid, posi, x);
else modify (rson[pre], rson[p], mid + 1, right, posi, x);
}
int query (int p, int left, int right, int posi) { // >= posi的和
if (right < posi) return 0;
if (posi <= left) return subsum[p];
int mid = (left + right) >> 1;
if (posi <= mid) return subsum[rson[p]] + query (lson[p], left, mid, posi);
else return query (rson[p], mid + 1, right, posi);
}
vector<int> rt[MAXN << 2], posi[MAXL + 10], use[MAXN << 2];
queue<int> que[MAXN];
int ldel[MAXN]= {0};
void build (int left, int right, int p, int d) {
if (left == right) return ;
int mid = (left + right) >> 1, n = posi[d].size();
use[p].resize(n); posi[d + 1].clear(), posi[d + 1].push_back(0);
for (int i = 1; i < n; i ++) {
int t = posi[d][i];
if (a[t] <= mid) {
posi[d + 1].push_back(t);
use[p][i] = use[p][i - 1] + 1;
ldel[a[t]] = 0;
}
else use[p][i] = use[p][i - 1];
}
int lim = posi[d + 1].size();
rt[p].resize(lim);
for (int i = 1; i < lim; i ++) {
int t = posi[d + 1][i], x = a[t];
que[x].push(i);
if ((int) que[x].size() > W) {
modify (rt[p][i - 1], rt[p][i], 1, lim - 1, que[x].front(), - W - 1);
if (ldel[x]) modify (rt[p][i], rt[p][i], 1, lim - 1, ldel[x], W);
ldel[x] = que[x].front(); que[x].pop();
}
else rt[p][i] = rt[p][i - 1];
}
for (int i = 1; i < lim; i ++) {
int t = posi[d + 1][i], x = a[t];
while (! que[x].empty()) que[x].pop();
}
build (left, mid, lp, d + 1);
posi[d + 1].clear(), posi[d + 1].push_back(0);
for (int i = 1; i < n; i ++) {
int t = posi[d][i];
if (a[t] > mid) posi[d + 1].push_back(t);
}
build (mid + 1, right, rp, d + 1);
}

int ans = 0;
int solve (int p, int left, int right, int L, int R, int k) {
if (left == right) return left;
int mid = (left + right) >> 1;
int l = use[p][L], r = use[p][R], n = rt[p].size();
int sum = r - l + query (rt[p][r], 1, n - 1, l + 1);
if (sum >= k) return solve (lp, left, mid, l, r, k);
else return solve (rp, mid + 1, right, L - l, R - r, k - sum);
}

inline int getnum () {
int num = 0; char ch = getchar ();
while (! isdigit (ch)) ch = getchar ();
while (isdigit (ch)) num = (num << 3) + (num << 1) + ch - '0', ch = getchar ();
return num;
}

int main () {
N = getnum (), W = getnum (), Q = getnum (), type = getnum ();
posi[1].resize(N + 1);
for (int i = 1; i <= N; i ++) { a[i] = getnum (); posi[1][i] = i; }
build (0, N, 1, 1);
for (int i = 1; i <= Q; i ++) {
int xxo = type * ans;
int l = getnum () ^ xxo, r = getnum () ^ xxo, k = getnum () ^ xxo;
ans = solve (1, 0, N, l - 1, r, k);
printf ("%d\n", ans);
}

return 0;
}

Problem B:求和

题面

题解

首先对式子进行变换
$$
\begin{aligned}
ans &= \sum\limits_{i = 1}^n\sum\limits_{j = 1}^n\sum\limits_{d = 1}^k f_d(gcd(i, j)) \\
&= \sum\limits_{x = 1}^n\sum\limits_{d = 1}^k f_d(x)\sum\limits_{i = 1}^{\lfloor\frac{n}{x}\rfloor}\sum\limits_{j = 1}^{\lfloor\frac{n}{x}\rfloor}[(i, j) = 1] \\
&= \sum\limits_{x = 1}^n\sum\limits_{d = 1}^k f_d(x)\left((2\sum\limits_{i = 1}^{\lfloor\frac{n}{x}\rfloor}\varphi(i)) - 1\right)
\end{aligned}
$$
那么这时对 $n$ 整除分块到,$\sum\limits_{i = 1}^n \varphi(i)$ 就可以直接杜教筛求出,设此时分块到 $[l, r]$,现在考虑求解 $\sum\limits_{x = l}^r\sum\limits_{d = 1}^k f_d(x)$,当然前缀和是肯定的

令 $F_d(x) = \sum\limits_{i = 1}^n f_d(i)$,设质因数分解 $n = \prod\limits_i p_i^{c_i}$,令 $\lambda(x) = \prod\limits_i (- 1)^{c_i}$,$F_d(x)$ 则可以通过容斥求出
$$
\begin{aligned}
F_d(x) &= \sum\limits_{i = 1}^x \mu(i)\sum\limits_{j = 1}^{\lfloor\frac{x}{i^{d + 1}}\rfloor}\lambda(i^{d + 1}j) \\
&= \sum\limits_{i = 1}^x \lambda^{d + 1}(i)\mu(i)\sum\limits_{j = 1}^{\lfloor\frac{x}{i^{d + 1}}\rfloor}\lambda(j)
\end{aligned}
$$
第一步实际上就是类似求 $n$ 以内无平方因子数的方法,$i = 1$ 时为总方案,后面容斥删去不合法

而第二步是因为 $\lambda(x)$ 是一个完全积性函数

现在问题转化为求 $\sum\limits_{i = 1}^n \lambda(i)$,可以发现一个性质,$\sum\limits_{d |n} \lambda(i) = [n为完全平方数]$

下面给出证明

  • 设 $n = \prod\limits_i p_i^{c_i}$,考虑两个数 $p_1^2x, p_1^3x$,满足 $p_1^2x, p_1^3x | n$,若 $p_1^2x$ 的贡献为 $1$,那么 $p_1^3x$ 的贡献则一定为 $- 1$,那么以此类推,也就是 $0, 1$ 次、$2, 3$ 次、$4, 5$ 次…分别两两配对,若 $c_i$ 为奇数,则最后贡献总和一定为 $0$,若为偶数,最后则一定为 $1$

这样的话就是 $\sqrt n = \lambda * 1$,就可以杜教筛了

那么对 $F_d(x)$,类似杜教筛预处理 $x$ 比较小的(注意用原式,即 $F_d(x) = \sum\limits_{i = 1}^n f_d(i)$ 来预处理),然后大的就直接暴力枚举,最多 $O (\sqrt n)$,那么这样总时间复杂度是 $O (n^{\frac23})$ 的

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>

#define MOD 1073741824

using namespace std;

typedef long long LL;
typedef unsigned int uint;

const int MAXN = 4e06 + 5e05 + 10;
const int MAXM = 3000 + 10;
const int MAXL = 1e05 + 10;

LL N; int K;

int prime[MAXN / 10], pcnt = 0;
bool visit[MAXN]= {false};
uint mu[MAXN]= {0};
uint phi[MAXN]= {0}, sphi[MAXN]= {0};
uint lamda[MAXN]= {0}, suml[MAXN]= {0};
uint sumf[MAXN]= {0}; // sumf(x): Σ(d=1~k)f[d](x)
int a[MAXN]= {0}, b[MAXN]= {0};
const int MAX = 4e06 + 5e05;
void sieve () {
mu[1] = phi[1] = lamda[1] = 1;
sumf[1] = K;
for (int i = 2; i <= MAX; i ++) {
if (! visit[i]) {
prime[++ pcnt] = i;
mu[i] = - 1;
phi[i] = i - 1, lamda[i] = - 1;
a[i] = b[i] = 1;
}
for (int j = 1; j <= pcnt && i * prime[j] <= MAX; j ++) {
visit[i * prime[j]] = true;
if (! (i % prime[j])) {
phi[i * prime[j]] = phi[i] * prime[j];
lamda[i * prime[j]] = - lamda[i];
a[i * prime[j]] = a[i] + 1;
b[i * prime[j]] = max (a[i * prime[j]], b[i]);
break;
}
mu[i * prime[j]] = - mu[i];
phi[i * prime[j]] = phi[i] * (prime[j] - 1);
lamda[i * prime[j]] = - lamda[i];
a[i * prime[j]] = 1;
b[i * prime[j]] = b[i];
}
}
for (int i = 1; i <= MAX; i ++) {
suml[i] = suml[i - 1] + lamda[i];
sphi[i] = sphi[i - 1] + phi[i];
sumf[i] = sumf[i - 1] + lamda[i] * max (0, K - max (b[i], 1) + 1);
}
}

bool vp[MAXM]= {false}, vl[MAXM]= {false};
uint sp[MAXM], sl[MAXM];
uint phi_sieve (LL n) {
if (n <= MAX) return sphi[n];
if (vp[N / n]) return sp[N / n];
uint ret = n * (n + 1) / 2;
for (LL l = 2, r; l <= n; l = r + 1) {
r = n / (n / l);
ret -= 1u * (r - l + 1) * phi_sieve (n / l);
}
vp[N / n] = true;
return sp[N / n] = ret;
}
uint lamda_sieve (LL n) {
if (n <= MAX) return suml[n];
if (vl[N / n]) return sl[N / n];
uint ret = sqrt (n);
for (LL l = 2, r; l <= n; l = r + 1) {
r = n / (n / l);
ret -= 1u * (r - l + 1) * lamda_sieve (n / l);
}
vl[N / n] = true;
return sl[N / n] = ret;
}

LL mpow[MAXL][42]= {0};
void init () {
int lim = sqrt (N) + 1;
for (int i = 1; i <= lim; i ++) {
mpow[i][0] = 1;
for (int j = 1; j <= K + 1; j ++) {
if (mpow[i][j - 1] > N) break;
mpow[i][j] = mpow[i][j - 1] * i;
}
}
}

bool vf[MAXM]= {false};
uint sf[MAXM];
uint work (LL n) {
if (n <= MAX) return sumf[n];
if (vf[N / n]) return sf[N / n];
uint ret = 0;
for (int d = 1; d <= K; d += 2)
for (int i = 1; mpow[i][d + 1] && mpow[i][d + 1] <= n; i ++)
ret += mu[i] * lamda_sieve (n / mpow[i][d + 1]);
for (int d = 2; d <= K; d += 2)
for (int i = 1; mpow[i][d + 1] && mpow[i][d + 1] <= n; i ++)
ret += lamda[i] * mu[i] * lamda_sieve (n / mpow[i][d + 1]);
vf[N / n] = true;
return sf[N / n] = ret;
}

int main () {
cin >> N >> K;
init (); sieve ();
uint ans = 0;
for (LL l = 1, r; l <= N; l = r + 1) {
r = N / (N / l);
ans += (2u * phi_sieve (N / l) - 1) * (work (r) - work (l - 1));
}
cout << ans % MOD << endl;

return 0;
}

Problem C: 树