打的太菜了1551,05一眼直接反演了然后化成了不可做的形式哭了,04傻逼了5点整才搞出来,结果差了几s
1001
按位考虑,发现这就是 A & B A \& B A&B,但是我们发现题目要求是正数,那么我们还需要把最低的一边是0一边是1的位改成1才行。
#include <iostream>
#include <cstdio>
#include <cmath>
#include <cstdio>
using namespace std;
int main() {
int t;
std::cin >> t;
while(t--) {
long long a, b, c = 0;
std::cin >> a >> b;
c = (a & b);
if (c == 0) {
auto t = (a ^ b);
c = t & -t;
}
// c = ((a ^ c) & (b ^ c));
std::cout << c << '\n';
}
}
1002
直接权值线段树维护最早出现的位置,每次线段树二分就行(因为是小洛洛写的没想到他居然不会写这个线段树二分,变成了手把手教队友写代码模式
#include <iostream>
#include <algorithm>
#include <vector>
#include <string>
#include <cstdio>
#include <cmath>
#include <cstdio>
#include <cstdint>
#include <utility>
#include <limits>
using namespace std;
// using i64 = int64_t;
using i64 = int;
const int INF = std::numeric_limits<int>::max();
class SegTree {
int sz, mx;
SegTree *l, *r;
void up () {
mx = std::max(l->mx, r->mx);
}
void down () {
if (!l) {
auto m = sz / 2;
l = new SegTree(m);
r = new SegTree(sz - m);
}
}
public:
SegTree (i64 sz): sz(sz), mx(INF), l(nullptr), r(nullptr) {}
~SegTree () { delete l; delete r; }
void set (i64 pos, i64 val) {
if (sz == 1) {
mx = val;
return;
}
down();
auto m = sz / 2;
if (pos < m) l->set(pos, val);
else r->set(pos - m, val);
up();
}
i64 magic (i64 k, i64 x) {
if (sz == 1)
return mx > x ? 0 : -1;
down();
auto m = sz / 2;
if (k == 0) {
if (l->mx > x)
return l->magic(0, x);
else if (r->mx > x)
return m + r->magic(0, x);
else
return -1;
} else if (k < m) {
auto ret = l->magic(k, x);
if (ret != -1)
return ret;
if (r->mx > x)
return m + r->magic(0, x);
return -1;
} else {
auto ret = r->magic(k - m, x);
return ret == -1 ? -1 : m + ret;
}
}
};
int main() {
std::ios::sync_with_stdio(false);
int t;
scanf("%d", &t);
while (t--) {
int n, m;
scanf("%d%d", &n, &m);
auto v = std::vector<int>(n + 1, false);
auto rt = new SegTree(n + 1);
for (int i = 1; i <= n; ++i) {
int c;
scanf("%d", &c);
v[i] = c;
rt->set(c, i);
}
i64 ans = 0;
for (int i = 0; i < m; ++i) {
int op, a, b;
scanf("%d%d", &op, &a);
if (op == 1) {
a ^= ans;
// std::cerr << ">1 " << a << ':' << v[a] << '\n';
if (v[a] != -1) {
rt->set(v[a], INF);
v[a] = -1;
}
} else {
scanf("%d", &b);
a ^= ans;
b ^= ans;
// std::cerr << ">2 " << a << ' ' << b << '\n';
ans = rt->magic(b, a);
if (ans == -1)
ans = n + 1;
printf("%d\n", ans);
}
}
delete rt;
}
return 0;
}
1003
最开始yy了一个SAM上倍增找点,主席树第k大的东西,但是一致认为太难写了,后来发现SA的话就相当于从rank[i]开始,往左往右找到第一个height小于它的位置,然后在这个区间中找到第k大就行。(后来发现题解就是那个SAM做法,果然我的SAM水平还是太弱了。
#include <iostream>
#include <cstdio>
#include <cmath>
#include <cstring>
#include <algorithm>
#include <cstdlib>
using namespace std;
int const N = 100005;
char s[N];
int n;
int rk[N], sa[N], height[N], w[N], cnt[N], res[N];
void getSa(int up) {
int *k = rk, *id = height, *r = res;
for (int i = 0; i < up; ++i) cnt[i] = 0;
for (int i = 0; i < n; ++i) cnt[k[i] = w[i]]++;
for (int i = 0; i < up; ++i) cnt[i + 1] += cnt[i];
for (int i = n - 1; i >= 0; --i) sa[--cnt[k[i]]] = i;
for (int d = 1; d <= n; d <<= 1) {
int p = 0;
for (int i = n - d; i < n; ++i) id[p++] = i;
for (int i = 0; i < n; ++i) if (sa[i] >= d) id[p++] = sa[i] - d;
for (int i = 0; i < n; ++i) r[i] = k[id[i]];
for (int i = 0; i < up; ++i) cnt[i] = 0;
for (int i = 0; i < n; ++i) cnt[r[i]]++;
for (int i = 0; i < up; ++i) cnt[i + 1] += cnt[i];
for (int i = n - 1; i >= 0; --i) sa[--cnt[r[i]]] = id[i];
swap(k, r);
p = 0;
k[sa[0]] = p++;
for (int i = 0; i < n - 1; ++i) {
if (sa[i] + d < n && sa[i + 1] + d < n && r[sa[i]] == r[sa[i + 1]] && r[sa[i] + d] == r[sa[i + 1] + d]) {
k[sa[i + 1]] = p - 1;
} else {
k[sa[i + 1]] = p++;
}
}
if (p >= n) return;
up = p;
}
}
void getHeight() {
for (int i = 0; i < n; ++i)
rk[sa[i]] = i;
height[0] = 0;
for (int i = 0, p = 0; i < n; ++i) {
if (rk[i] == 0) continue;
int j = sa[rk[i] - 1];
while (i + p < n && j + p < n && w[i + p] == w[j + p]) ++p;
height[rk[i]] = p;
p = max(0, p - 1);
}
}
void getSuffix(char s[]) {
n = strlen(s);
int up = 0;
for (int i = 0; i < n; ++i) {
w[i] = s[i];
up = max(up, w[i]);
}
getSa(up + 1);
getHeight();
}
int f[N][21];
int RMQ(int l, int r) {
int k = 0;
while (1 << (k + 1) <= r - l + 1) ++k;
int x = f[l][k], y = f[r - (1 << k) + 1][k];
return min(height[x - 1], height[y - 1]);
}
void DP() {
for (int i = 1; i <= n; ++i) f[i][0] = i;
for (int j = 1; (1 << j) <= n; ++j)
for (int i = 1; i + (1 << j) - 1 <= n; ++i) {
int x = f[i][j - 1], y = f[i + (1 << (j - 1))][j - 1];
f[i][j] = height[x - 1] < height[y - 1] ? x : y;
}
}
int getLef(int pos, int val) {
int ret = 1, l = 1, r = pos;
while (l <= r) {
int mid = (l + r) >> 1;
if (RMQ(mid, pos) < val) {
ret = mid;
l = mid + 1;
} else {
r = mid - 1;
}
}
return ret;
}
int getRig(int pos, int val) {
int ret = n + 1, l = pos + 1, r = n;
while (l <= r) {
int mid = (l + r) >> 1;
if (RMQ(pos + 1, mid) < val) {
ret = mid;
r = mid - 1;
} else {
l = mid + 1;
}
}
return ret;
}
struct node {
int lc, rc, s;
} t[N * 32];
int root[N], segn;
void insert(int& k, int lk, int l, int r, int pos) {
k = ++segn;
t[k] = t[lk];
t[k].s++;
if (l == r) return;
int mid = (l + r) >> 1;
if (pos <= mid) {
insert(t[k].lc, t[lk].lc, l, mid, pos);
} else {
insert(t[k].rc, t[lk].rc, mid + 1, r, pos);
}
}
int query(int k1, int k2, int l, int r, int k) {
if (l == r) return l;
int mid = (l + r) >> 1;
int sum = t[t[k1].lc].s - t[t[k2].lc].s;
if (k <= sum) {
return query(t[k1].lc, t[k2].lc, l, mid, k);
} else {
return query(t[k1].rc, t[k2].rc, mid + 1, r, k - sum);
}
}
int main() {
int T;
scanf("%d", &T);
while (T--) {
int Q;
scanf("%d%d", &n, &Q);
for (int i = 0; i <= n; ++i) {
sa[i] = 0;
rk[i] = 0;
height[i] = 0;
root[i] = 0;
}
scanf("%s", s);
getSuffix(s);
DP();
segn = 0;
t[0] = { 0, 0, 0 };
for (int i = 0; i < n; ++i) {
insert(root[i + 1], root[i], 1, n, sa[i] + 1);
}
while (Q--) {
int l, r, k;
scanf("%d%d%d", &l, &r, &k);
int pos = rk[l - 1];
int ql = getLef(pos + 1, r - l + 1);
int qr = getRig(pos + 1, r - l + 1);
if (t[root[qr - 1]].s - t[root[ql - 1]].s < k) {
puts("-1");
continue;
}
printf("%d\n", query(root[qr - 1], root[ql - 1], 1, n, k));
}
}
}
1004
显然就把所有的边最开始加进堆里,把每个点的出边按权值排序,每次的状态表示为
(
l
e
n
,
e
n
d
p
o
i
n
t
,
K
,
l
a
s
t
b
e
g
i
n
p
o
i
n
t
)
(len, endpoint, K, lastbeginpoint)
(len,endpoint,K,lastbeginpoint)
分别表示当前路径的长度,路径的终点,以及最后一条边是从
l
a
s
t
b
e
g
i
n
p
o
i
n
t
lastbeginpoint
lastbeginpoint开始到
e
n
d
p
o
i
n
t
endpoint
endpoint的,而且是
l
a
s
t
b
e
g
i
n
p
o
i
n
t
lastbeginpoint
lastbeginpoint的第K大的边。那么只有两种转移,要么把第
K
K
K条边变成
l
a
s
t
b
e
g
i
n
p
o
i
n
t
lastbeginpoint
lastbeginpoint的第
K
+
1
K+1
K+1条边,要么往后延伸一条边。
但是要注意最开始加入的边是不能有第一种转移的!(草搞了我好久最后在4:58意识到问题5:00改了出来)
#include <iostream>
#include <cstdio>
#include <cmath>
#include <cstring>
#include <algorithm>
#include <cstdlib>
#include <vector>
#include <queue>
#include <functional>
using namespace std;
int const N = 100005;
struct Ed {
int x, y, w;
} b[N];
struct edge {
int y, w;
bool operator < (edge const& oth) const {
return w < oth.w;
}
};
vector<edge> G[N];
int n, m, Q;
struct ques {
int k;
long long ans;
int id;
bool operator < (ques const& oth) const {
return k < oth.k;
}
} q[N];
bool cmpid(ques const& x, ques const& y) {
return x.id < y.id;
}
struct node {
long long len;
int y, k, las;
bool f;
bool operator < (node const& oth) const {
return len < oth.len;
}
bool operator > (node const& oth) const {
return len > oth.len;
}
};
priority_queue<node, vector<node>, greater<node> > que;
int main() {
int T;
scanf("%d", &T);
while (T--) {
scanf("%d%d%d", &n, &m, &Q);
while (que.empty() == 0) que.pop();
for (int i = 1; i <= m; ++i) {
scanf("%d%d%d", &b[i].x, &b[i].y, &b[i].w);
G[b[i].x].push_back({ b[i].y, b[i].w });
}
for (int i = 1; i <= n; ++i) sort(G[i].begin(), G[i].end());
for (int i = 1; i <= n; ++i) {
for (int j = 0; j < (int)G[i].size(); ++j) {
que.push({ G[i][j].w, G[i][j].y, j, i, 0 });
}
}
for (int i = 1; i <= Q; ++i) {
scanf("%d", &q[i].k);
q[i].id = i;
}
sort(q + 1, q + Q + 1);
int p = 1, np = 0;
while (p <= Q && que.empty() == 0) {
auto now = que.top();
que.pop();
++np;
while (p <= Q && q[p].k == np) {
q[p].ans = now.len;
++p;
}
if (G[now.y].size() > 0u)
que.push({ now.len + G[now.y][0].w, G[now.y][0].y, 0, now.y, 1 });
if (now.f && now.k + 1 < (int)G[now.las].size()) {
que.push({ now.len + G[now.las][now.k + 1].w - G[now.las][now.k].w, G[now.las][now.k + 1].y, now.k + 1, now.las, 1 });
}
}
sort(q + 1, q + Q + 1, cmpid);
for (int i = 1; i <= Q; ++i)
printf("%lld\n", q[i].ans);
for (int i = 1; i <= n; ++i)
G[i].clear();
}
}
1005
以后傻子才直接看到互质就莫反。
update:补了直接莫反的做法。(今早直接去刷了点杜教筛的题发现还是我太垃圾)
首先大力猜一个结论,那就是
g
c
d
(
a
n
−
b
n
,
a
m
−
b
m
)
=
a
g
c
d
(
n
,
m
)
−
b
g
c
d
(
n
,
m
)
gcd(a^n-b^n, a^m-b^m) =a^{gcd(n,m)}-b^{gcd(n,m)}
gcd(an−bn,am−bm)=agcd(n,m)−bgcd(n,m)
虽然这个式子很难猜,但是在这里因为
n
n
n和
m
m
m是互质的,猜是
a
−
b
a-b
a−b还是很容易的
考虑直接拆开式子
∑
i
=
1
n
∑
j
=
1
i
(
i
−
j
)
[
g
c
d
(
i
,
j
)
=
=
1
]
\sum_{i=1}^n \sum_{j = 1}^i (i - j) [gcd(i, j)==1]
i=1∑nj=1∑i(i−j)[gcd(i,j)==1]
=
∑
i
=
1
n
∑
j
=
1
i
i
[
g
c
d
(
i
,
j
)
=
=
1
]
−
∑
i
=
1
n
∑
j
=
1
i
j
[
g
c
d
(
i
,
j
)
=
=
1
]
=\sum_{i=1}^n \sum_{j = 1}^i i [gcd(i, j)==1] - \sum_{i=1}^n \sum_{j = 1}^i j [gcd(i, j)==1]
=i=1∑nj=1∑ii[gcd(i,j)==1]−i=1∑nj=1∑ij[gcd(i,j)==1]
=
∑
i
=
1
n
i
φ
(
i
)
−
∑
i
=
1
n
i
∗
φ
(
i
)
+
[
i
=
=
1
]
2
=\sum_{i=1}^n i \varphi(i) - \sum_{i=1}^n \frac{i * \varphi(i) + [i == 1]}{2}
=i=1∑niφ(i)−i=1∑n2i∗φ(i)+[i==1]
=
∑
i
=
1
n
i
∗
φ
(
i
)
−
1
2
=\frac{\sum_{i=1}^ni * \varphi(i) - 1}{2}
=2∑i=1ni∗φ(i)−1
然后我们给
i
∗
φ
(
i
)
i*\varphi(i)
i∗φ(i)卷上
i
d
id
id就可以杜教筛了。
直接莫反的话会得到
1
2
∗
∑
d
=
1
n
d
∗
μ
[
d
]
∗
∑
i
=
1
[
n
d
]
i
2
−
i
\frac{1}{2} * \sum_{d = 1}^n d * \mu[d] * \sum_{i = 1}^{[\frac{n}{d}]} i ^ 2 - i
21∗d=1∑nd∗μ[d]∗i=1∑[dn]i2−i
同样可以直接数论分块,对于左边的部分卷上
i
d
id
id就可以杜教筛了。代码见第二份
#include <iostream>
#include <cstdio>
#include <cmath>
#include <cstring>
#include <cstdlib>
#include <algorithm>
using namespace std;
using ll = long long;
int const M = 1000000;
int const N = M + 5;
ll const mod = 1e9 + 7;
ll KSM(ll a, ll k) {
ll ret = 1;
for (; k; k >>= 1, a = a * a % mod)
if (k & 1)
ret = ret * a % mod;
return ret;
}
ll f[N];
int pri[N], tot;
bool isp[N];
ll n, inv2, inv3;
void SAI() {
isp[1] = 1;
f[1] = 1;
for (int i = 2; i <= M; ++i) {
if (isp[i] == 0) {
pri[++tot] = i;
f[i] = i - 1;
}
for (int j = 1; j <= tot && i * pri[j] <= M; ++j) {
isp[i * pri[j]] = 1;
if (i % pri[j])
f[i * pri[j]] = f[i] * f[pri[j]];
else {
f[i * pri[j]] = f[i] * pri[j];
break;
}
}
}
for (int i = 1; i <= M; ++i)
f[i] = (1ll * i * f[i] % mod + f[i - 1]) % mod;
}
#define G 83333
ll p1[G], p2[G];
ll& gm(ll x) {
if (x < G)
return p1[x];
else
return p2[n / x];
}
ll sum_(ll x) {
if (x <= M)
return f[x];
ll& qs = gm(x);
if (qs != -1)
return qs;
ll ret = x * (x + 1) % mod * inv2 % mod * (2 * x + 1) % mod * inv3 % mod;
for (ll l = 2, r; l <= x; l = r + 1) {
r = x / (x / l);
ret -= (l + r) * (r - l + 1) % mod * inv2 % mod * sum_(x / l) % mod;
ret %= mod;
}
ret = (ret + mod) % mod;
return qs = ret;
}
ll get_sum(ll x) {
if (x <= M)
return f[x];
memset(p1, -1, sizeof(p1));
memset(p2, -1, sizeof(p2));
return sum_(x);
}
int main() {
SAI();
int T;
scanf("%d", &T);
inv2 = KSM(2, mod - 2);
inv3 = KSM(3, mod - 2);
while (T--) {
ll t1, t2;
scanf("%lld%lld%lld", &n, &t1, &t2);
ll ans = (get_sum(n) - 1 + mod) % mod;
ans = ans * inv2 % mod;
printf("%lld\n", ans);
}
}
直接莫反版本
#include <iostream>
#include <cstdio>
#include <cmath>
#include <cstring>
#include <cstdlib>
#include <algorithm>
#include <unordered_map>
using namespace std;
using ll = long long;
int const M = 1e6;
int const N = M + 5;
int const mod = 1e9 + 7;
ll KSM(ll a, ll k) {
ll ret = 1;
for (; k; k >>= 1, a = a * a % mod)
if (k & 1)
ret = ret * a % mod;
return ret;
}
ll f[N];
int pri[N], tot;
bool isp[N];
ll inv2, inv3;
void SAI() {
isp[1] = 1;
f[1] = 1;
for (int i = 2; i <= M; ++i) {
if (isp[i] == 0) {
pri[++tot] = i;
f[i] = -1;
}
for (int j = 1; j <= tot && i * pri[j] <= M; ++j) {
isp[i * pri[j]] = 1;
if (i % pri[j])
f[i * pri[j]] = -f[i];
else {
f[i * pri[j]] = 0;
break;
}
}
}
for (int i = 1; i <= M; ++i)
f[i] = (1ll * i * f[i] % mod + f[i - 1]) % mod;
}
unordered_map<int, int> mp;
int sum_(int x) {
if (x <= M)
return f[x];
if (mp.count(x) > 0)
return mp[x];
int ret = 1;
for (int l = 2, r; l <= x; l = r + 1) {
r = x / (x / l);
ret = (ret - 1ll * (r - l + 1) * (l + r) % mod * inv2 % mod * sum_(x / l) % mod) % mod;
}
ret = (ret + mod) % mod;
mp[x] = ret;
return ret;
}
int main() {
SAI();
int T;
scanf("%d", &T);
inv2 = KSM(2, mod - 2);
inv3 = KSM(3, mod - 2);
while (T--) {
int n, t1, t2;
scanf("%d%d%d", &n, &t1, &t2);
int ans = 0;
for (int l = 1, r; l <= n; l = r + 1) {
r = n / (n / l);
int nd = n / l;
t2 = 1ll * (1 + nd) * nd % mod * inv2 % mod;
t1 = 1ll * t2 * (2 * nd + 1) % mod * inv3 % mod;
t1 = t1 - t2;
t1 = t1 < 0 ? t1 + mod : t1;
ans = (ans + 1ll * t1 * (sum_(r) - sum_(l - 1))) % mod;
}
ans = (1ll * ans * inv2 % mod + mod) % mod;
printf("%d\n", ans);
}
}
1006
#include <iostream>
#include <algorithm>
#include <vector>
#include <string>
#include <cstdio>
#include <cmath>
#include <cstdio>
#include <cstdint>
#include <utility>
using namespace std;
using i64 = int64_t;
using Pr = std::pair<int, int>;
int main() {
std::ios::sync_with_stdio(false);
int n, m;
std::cin >> n >> m;
auto idx = std::vector<Pr>(n);
for (int i = 0; i < n; ++i) {
int c;
std::cin >> c;
idx[c - 1] = { i, c };
}
int mn = 0;
while(m--) {
int c;
std::cin >> c;
idx[c - 1].first = --mn;
}
std::sort(std::begin(idx), std::end(idx));
for(auto &c: idx)
std::cout << c.second << ' ';
// std::cout << '\n';
return 0;
}
1007
#include <iostream>
#include <vector>
#include <string>
#include <cstdio>
#include <cmath>
#include <cstdio>
#include <cstdint>
using namespace std;
using i64 = int64_t;
using Mat = std::vector<std::string>;
void ccpc(Mat &m, int k, int x, int y, int idx) {
const char *const CHR = "CP";
if (k == 0)
m[x][y] = CHR[idx];
else {
int half = 1 << (k - 1);
ccpc(m, k - 1, x, y, idx);
ccpc(m, k - 1, x, y + half, idx);
ccpc(m, k - 1, x + half, y, !idx);
ccpc(m, k - 1, x + half, y + half, idx);
}
}
int main() {
int t;
std::cin >> t;
while(t--) {
int k;
std::cin >> k;
auto mat = Mat(1 << k, std::string(1 << k, '.'));
ccpc(mat, k, 0, 0, 0);
for (auto &row: mat)
std::cout << row << '\n';
}
}
1008
每次把小段拿出来搞就行
#include <iostream>
#include <algorithm>
#include <stdio.h>
#include <queue>
using namespace std;
const int maxn = 100010;
int t[maxn];
priority_queue<int> q;
bool cmp(int a,int b) {return a > b;}
int main() {
int T;
scanf("%d",&T);
while(T--) {
while(!q.empty()) q.pop();
int n,k;
scanf("%d%d",&n,&k);
for(int i = 0;i < n;i++) {
scanf("%d",&t[i]);
}
sort(t,t+n,cmp);
long long ans = 0;
long long fish = 0;
for(int i = 0;i < n;i++) {
if(fish == 0) {
if(q.empty()) ans += k;
else {
ans += k-q.top();
q.pop();
}
fish++;
}
fish += t[i]/k;
ans += t[i];
if(t[i]%k != 0) q.push(t[i]%k);
fish--;
}
printf("%lld\n",ans);
}
return 0;
}
1009
1010
我们可以设区间内
c
i
c_i
ci为
i
i
i这个数出现的次数。
那么就会有
∑
i
=
l
r
∑
j
=
l
r
φ
(
g
c
d
(
a
i
,
a
j
)
)
l
c
m
(
a
i
,
a
j
)
\sum_{i = l}^r \sum_{j = l}^r \varphi(gcd(a_i, a_j))lcm(a_i, a_j)
i=l∑rj=l∑rφ(gcd(ai,aj))lcm(ai,aj)
=
∑
d
=
1
n
∑
i
=
1
n
∑
j
=
1
n
[
g
c
d
(
i
,
j
)
=
=
d
]
c
i
c
j
φ
(
d
)
i
j
d
=\sum_{d = 1}^n \sum_{i = 1}^n \sum_{j = 1}^n [gcd(i, j) == d] c_i c_j \varphi(d) \frac{ij}{d}
=d=1∑ni=1∑nj=1∑n[gcd(i,j)==d]cicjφ(d)dij
然后大力反演,就会有
=
∑
d
=
1
d
φ
(
d
)
∑
T
=
1
[
n
/
d
]
T
2
μ
[
T
]
∑
i
=
1
n
/
d
T
∑
j
=
1
n
/
d
T
c
i
T
d
c
j
T
d
i
j
=\sum_{d = 1} d \varphi(d) \sum_{T = 1}^{[n / d]} T ^ 2 \mu[T] \sum_{i =1}^{n / dT} \sum_{j =1}^{n / dT} c_{i T d} c_{j T d} ij
=d=1∑dφ(d)T=1∑[n/d]T2μ[T]i=1∑n/dTj=1∑n/dTciTdcjTdij
然后显然后面那两块是一样的,那么就是
=
∑
d
=
1
d
φ
(
d
)
∑
T
=
1
[
n
/
d
]
T
2
μ
[
T
]
(
∑
i
=
1
n
/
d
T
c
i
T
d
i
)
2
=\sum_{d = 1} d \varphi(d) \sum_{T = 1}^{[n / d]} T ^ 2 \mu[T] (\sum_{i =1}^{n / dT} c_{i T d} i) ^ 2
=d=1∑dφ(d)T=1∑[n/d]T2μ[T](i=1∑n/dTciTdi)2
然后我们设
j
=
T
d
j = Td
j=Td
这样就化成了
∑
j
=
1
n
j
∑
T
∣
j
T
μ
[
T
]
φ
[
j
T
]
(
∑
i
=
1
n
/
d
T
c
i
T
d
i
)
2
\sum_{j = 1}^n j \sum_{T | j} T \mu[T] \varphi[\frac{j}{T}] (\sum_{i =1}^{n / dT} c_{i T d} i) ^ 2
j=1∑njT∣j∑Tμ[T]φ[Tj](i=1∑n/dTciTdi)2
但是中间那里的
∑
T
∣
j
T
μ
[
T
]
φ
[
j
T
]
\sum_{T | j} T \mu[T] \varphi[\frac{j}{T}]
∑T∣jTμ[T]φ[Tj]实际上就是
f
∗
φ
=
f
∗
i
d
∗
μ
=
e
∗
μ
=
μ
f * \varphi = f * id * \mu = e * \mu = \mu
f∗φ=f∗id∗μ=e∗μ=μ
所以式子实际上就是
∑
j
=
1
n
j
μ
[
j
]
(
∑
i
=
1
[
n
/
j
]
i
c
i
j
)
2
\sum_{j = 1}^n j \mu[j] (\sum_{i = 1}^{[n/ j]} i c_{ij})^ 2
j=1∑njμ[j](i=1∑[n/j]icij)2
然后考虑莫队维护答案即可,就算算增量就完事了。
不过upd那个函数写好看点才行,不然太容易T了。
#include <iostream>
#include <cstdio>
#include <cmath>
#include <cstring>
#include <cstdlib>
#include <algorithm>
#include <cassert>
#include <ctime>
#include <vector>
#include <utility>
using namespace std;
int const N = 100005;
int const M = 10000000;
int read() {
int d = 0;
char s = getchar();
while (s < '0' || s > '9')
s = getchar();
while (s >= '0' && s <= '9') {
d = d * 10 + s - '0';
s = getchar();
}
return d;
}
int mu[M + 5], pri[M / 3], tot;
bool isp[M + 5];
void SAI() {
isp[1] = 1;
mu[1] = 1;
for (int i = 2; i <= M; ++i) {
if (isp[i] == 0) {
mu[i] = -1;
pri[++tot] = i;
}
for (int j = 1; j <= tot && i * pri[j] <= M; ++j) {
isp[i * pri[j]] = 1;
if (i % pri[j])
mu[i * pri[j]] = -mu[i];
else {
mu[i * pri[j]] = 0;
break;
}
}
}
}
struct ques {
int l, r, id, posl;
unsigned int ans;
} q[N];
int bsz;
bool cmpq(ques const& a, ques const& b) {
if (a.posl == b.posl)
return a.r < b.r;
return a.posl < b.posl;
}
bool cmpid(ques const& a, ques const& b) {
return a.id < b.id;
}
unsigned int a[N];
vector<pair<unsigned int, unsigned int> > p[N];
int n, m;
unsigned int ans;
unsigned int d[M];
inline void upd(int k, unsigned int op) {
for (auto const& pr : p[k]) {
unsigned int i = a[k] / pr.first * op;
ans += pr.second * i * (d[pr.first] * 2u + i);
d[pr.first] += i;
}
}
inline void solve() {
ans = 0;
upd(1, 1);
for (int i = 1, L = 1, R = 1; i <= m; ++i) {
for (; R < q[i].r; ++R)
upd(R + 1, 1);
for (; L > q[i].l; --L)
upd(L - 1, 1);
for (; R > q[i].r; --R)
upd(R, -1);
for (; L < q[i].l; ++L)
upd(L, -1);
q[i].ans = ans;
}
}
int main() {
SAI();
int T = read();
while (T--) {
n = read(), m = read();
for (int i = 1; i <= n; ++i) {
a[i] = read();
p[i].clear();
for (int j = 1; j * j <= a[i]; ++j)
if (a[i] % j == 0) {
if (mu[j] != 0)
p[i].emplace_back(j, j * mu[j]);
if (j * j != a[i] && mu[a[i] / j] != 0)
p[i].emplace_back(a[i] / j, a[i] / j * mu[a[i] / j]);
d[j] = d[a[i] / j] = 0;
}
}
bsz = sqrt(m);
for (int i = 1; i <= m; ++i) {
q[i].l = read();
q[i].r = read();
q[i].posl = (q[i].l - 1) / bsz + 1;
q[i].id = i;
q[i].ans = 0;
}
sort(q + 1, q + m + 1, cmpq);
solve();
sort(q + 1, q + m + 1, cmpid);
for (int i = 1; i <= m; ++i)
printf("%u\n", q[i].ans);
}
}