[APIO2011]方格染色
有一种独特的角度,对于 (x, y ge 2) 的点对,从左上角到这个位置的矩形所有位置填满 (2 * 2) 的小正方形,发现除了四个角,剩下都被覆盖了偶数次。设小正方形数量奇偶性为 (a),那么就有结论 ((1, 1) ext{ xor } (x, 1) ext{ xor } (1, y) ext{ xor } (x, y) = a)。枚举 ((1,1)) 的值,然后就把这个关系转化为第一行第一列两个变量的关系,用扩展域并查集。对于在第一行第一列的点,如果他被固定,那么他所在联通块就不能活动,可以认为将其与 ((1, 1)) 建立关系。所以两方面是统一的,而题解没有说清楚。
[APIO2008]免费道路
(1) 边连成所有联通块后,需要必须的边数 (a) 将其连起来。
三种无解:
- 能放到一颗树里的边 (< K)
- (a > K)
- 图不联通
剩下的情况,相当于添加 (K - a) 条 (0) 边来代替 (1) 的作用,肯定可以做到,因为没有合并 (0) 边前他都合并起来了,所以有了他更行了。
[APIO2009]会议中心
这题教会我怎么快速求区间内最多不相交线段数。
考虑把包含线段去掉,剩下的是左右端点递增的区间,这样每个区间有了唯一的后继,就可以做倍增了(选择第一个左端点大于自身右端点)
[APIO2011]寻路
关键是发现性质,肯定不会在没有出现过的沿着横纵坐标上走,因为这样肯定到不了终点。
因此只要在离散化的点(有边的)上建图就好了,点边都是 ((N ^ 2)),每个点向最近的上下左右四个位置建边即可。
[APIO2009]采油区域
三个正方形可以把整个矩形恰好分成三个矩形,每个矩形求最大值即可。
分类讨论 (6) 种分割方案。
[NOI2008]假面舞会
题解有问题,并不能找出所有的环。
我迄今为止还不知道为啥这样可以找到所有环长 gcd。
[NOI2016] 循环之美
(frac{x}{y}) 相同的值只统计一次 (Rightarrow) 只统计 (x ⊥ y) 的点对。
(frac{x}{y}) 在 (K) 进制下是纯循环小数 (Rightarrow) 模仿竖式计算,考虑纯循环的充要条件就是在不断除的过程中碰到相同的余数,即存在正整数 (t),满足 (x equiv xk^t mod y Leftrightarrow k^t equiv 1 mod y Leftrightarrow k ⊥ y)。
最后一步右推左,(t = varphi(y)) 就好了,左推右考虑 (gcd(y, k^t) ot= 1) 的情况他们的减法也必然是 ( ext{gcd}) 的倍数不可能是 (1)。
故 $$ ext{Ans} = displaystyle sum_{x=1}^n sum_{y=1}^m [x ⊥ y] [y ⊥ k] = displaystyle sum_{y=1}^m [y ⊥ k] sum_{x=1}^n sum_{d|x, d|y} mu(d) displaystyle = sum_{d=1}^{min(n,m)} mu(d) [d ⊥ k] lfloor frac{n}{d} floor sum_{y=1}^{lfloor frac{m}{d} floor} [y ⊥ k]$$
这很类似一个数论分块的形式,设 (f(n, k) = displaystyle sum_{i=1}^{n} mu(i) [i ⊥ k], g(n, k) = displaystyle sum_{i=1}^{n} [i ⊥ k])
那么 ( ext{Ans} = displaystyle sum_{d=1}^{min(n,m)} mu(d) [d ⊥ k] lfloor frac{n}{d} floor g(lfloor frac{m}{d} floor, k))
只要能快速在关键点处 (约为 (sqrt{n}) 级别)的前缀和 (假设 (O(a))),那么就可以 (O(asqrt{n})) 做了。
考虑 (f, g) 怎么求,第二维是 (1) 的话,后者就是 (n),前者可以杜教筛求出,注意用到的 (n) 必然是 (n, m) 除一个数下取整的数,因此是 (sqrt{n}) 级别,杜教筛 (O(n^{frac{2}{3}}))。
然后考虑先把 (K) 的平方因子去掉,互质性质不变,考虑从 (K) 里去掉一个素因子的影响:
-
(f(n, k) = f(n, k / p) + f(lfloor frac{n}{p} floor, k))
-
(g(n, k) = g(n, k / p) + g(lfloor frac{n}{p} floor, k / p))
会用到的 (k) 大概是原来 (K) 的质因子级别(考虑这个质因子都选最小质因子),每次递推是 (O(1)),因此总复杂度大概是 (O(n^{frac{2}{3}} + sqrt{n} log k))。
#include <iostream>
#include <cstdio>
#include <map>
#include <cstring>
using namespace std;
typedef long long LL;
const int S = 1e7 + 1, T = 2005, INF = 0x3f3f3f3f;
int primes[S], tot, fac[S], mu[S];
bool st[S];
int gcd(int a, int b) {
return b ? gcd(b, a % b) : a;
}
map<int, int> F[T], G[T], mu2;
LL ans;
LL f(int n, int k) {
if (n == 0) return 0;
if (n == 1) return 1;
if (F[k].count(n)) return F[k][n];
else if (k > 1) {
int p = fac[k];
return F[k][n] = (f(n, k / p) + f(n / p, k));
} else {
if (n < S) return mu[n];
int res = 1;
for (int l = 2, r, t; l <= n; l = r + 1) {
t = n / l, r = n / t;
res -= f(t, k) * (r - l + 1);
}
return F[k][n] = res;
}
}
LL g(int n, int k) {
if (n == 0) return 0;
if (G[k].count(n)) return G[k][n];
else if (k > 1) {
int p = fac[k];
return G[k][n] = (g(n, k / p) - g(n / p, k / p));
} else return n;
}
void init() {
for (int i = 1; i < S; i++) mu[i] = 1;
for (int i = 2; i < S; i++) {
if (!st[i]) primes[++tot] = i, mu[i] = -1, fac[i] = i;
for (int j = 1; primes[j] * i < S; j++) {
st[primes[j] * i] = true;
fac[primes[j] * i] = primes[j];
if (i % primes[j] == 0) {
mu[i * primes[j]] = 0;
break;
}
mu[i * primes[j]] = -mu[i];
}
}
for (int i = 2; i < S; i++) mu[i] += mu[i - 1];
}
int main() {
init();
int n, m, K; scanf("%d%d%d", &n, &m, &K);
for (int i = 2; i <= K; i++) {
while (K % i == 0 && (K / i) % i == 0) K /= i;
}
for (int l = 1, r, a, b; l <= min(n, m); l = r + 1) {
a = n / l, b = m / l, r = min(min(n, m), min(n / a, m / b));
ans += a * (f(r, K) - f(l - 1, K)) * g(b, K);
}
printf("%lld
", ans);
return 0;
}
[SDOI2018]旧试题
草数学公式写的太累了。
跟循环之美大致类似,要用到约数个数和的 trick。(d(i, j, k) = displaystyle sum_{x|i} sum_{y|j} sum_{z|k} [x ⊥ y] [x ⊥ z] [y ⊥ z])。这个可以扩展到 (n) 元,大致就是每个质因子独立,然后恰好出现的次数是对的。
可以拿非平方因子数来建一颗数,这样每次增加一层的就是 (O(n sqrt{n}))
#include <iostream>
#include <cstdio>
#include <unordered_map>
#include <algorithm>
#include <cstring>
#include <vector>
using namespace std;
typedef long long LL;
const int S = 1e5 + 1, T = 1e5 + 1, INF = 0x3f3f3f3f, P = 1e9 + 7;
int primes[S], tot, fac[S], mu[S], h[S], d[8], top[8], A[8][S * 20], B[8][S * 20];
bool st[S], vis[S];
int F[8][T], G[8][T];
vector<int> e[S];
int s[S], ans;
int a, b, c;
int f(int n, int k) {
if (n == 0) return 0;
if (n == 1) return 1;
if (k == 0) return mu[n];
if (F[k][n] != -1) return F[k][n];
else {
++top[k];
A[k][top[k]] = k, B[k][top[k]] = n;
int p = fac[d[k]];
return F[k][n] = (f(n, k - 1) + f(n / p, k)) % P;
}
}
void init() {
for (int i = 1; i < S; i++) mu[i] = 1;
for (int i = 2; i < S; i++) {
if (!st[i]) primes[++tot] = i, mu[i] = -1, fac[i] = i;
for (int j = 1; primes[j] * i < S; j++) {
st[primes[j] * i] = true;
fac[primes[j] * i] = primes[j];
if (i % primes[j] == 0) {
mu[i * primes[j]] = 0;
break;
}
mu[i * primes[j]] = -mu[i];
}
}
for (int i = 2; i < S; i++) (mu[i] += mu[i - 1] + P) %= P;
}
int g(int n, int k) {
if (n == 0) return 0;
if (G[k][n] != -1) return G[k][n];
else if (k > 0) {
++top[k];
A[k][top[k]] = k, B[k][top[k]] = n;
int p = fac[d[k]];
return G[k][n] = ((LL)g(n, k - 1) - g(n / p, k - 1) + P) % P;
} else {
++top[k];
A[k][top[k]] = k, B[k][top[k]] = n;
int res = 0;
for (int l = 1, r, t; l <= n; l = r + 1) {
t = n / l, r = n / t;
res = (res + t * (r - l + 1ll)) % P;
}
return G[k][n] = res;
}
}
void dfs(int u, int dep) {
top[dep] = 0; int s = 0;
d[dep] = u;
for (int l = 1, r, t, v; l <= min(b, c); l = r + 1) {
t = b / l, v = c / l, r = min(min(b, c), min(b / t, c / v));
s = (s + ((LL)f(r, dep) - f(l - 1, dep) + P) * g(t, dep) % P * g(v, dep)) % P;
}
h[u] = s;
for (int i = 0; i < e[u].size(); i++) {
int v = e[u][i];
dfs(v, dep + 1);
}
for (int j = 1; j <= top[dep]; j++)
F[A[dep][j]][B[dep][j]] = -1, G[A[dep][j]][B[dep][j]] = -1;
}
int main() {
memset(F, -1, sizeof F);
memset(G, -1, sizeof G);
init();
int T; scanf("%d", &T);
while (T--) {
scanf("%d%d%d", &a, &b, &c);
for (int i = 1; i <= a; i++) vis[i] = false, e[i].clear();
ans = 0;
for (int i = 1; i <= a; i++) {
int s = 0; int x = i;
for (int j = 2; j * j <= x; j++)
while (x % j == 0 && (x / j) % j == 0) x /= j;
if (!vis[x] && fac[x] > 1) vis[x] = true, e[x / fac[x]].push_back(x);
}
dfs(1, 0);
for (int i = 1; i <= a; i++) {
int s = 0; int x = i;
for (int j = 2; j * j <= x; j++)
while (x % j == 0 && (x / j) % j == 0) x /= j;
ans = (ans + (LL)h[x] * (a / i)) % P;
}
printf("%d
", ans);
}
}