刷题记录

[APIO2011]方格染色

有一种独特的角度,对于 \(x, y \ge 2\) 的点对,从左上角到这个位置的矩形所有位置填满 \(2 * 2\) 的小正方形,发现除了四个角,剩下都被覆盖了偶数次。设小正方形数量奇偶性为 \(a\),那么就有结论 \((1, 1) \text{ xor } (x, 1) \text{ xor } (1, y) \text{ 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 \bmod y \Leftrightarrow k^t \equiv 1 \bmod y \Leftrightarrow k ⊥ y\)

最后一步右推左,\(t = \varphi(y)\) 就好了,左推右考虑 \(\gcd(y, k^t) \not= 1\) 的情况他们的减法也必然是 \(\text{gcd}\) 的倍数不可能是 \(1\)

故 $$\text{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} \rfloor \sum_{y=1}^{\lfloor \frac{m}{d} \rfloor} [y ⊥ k]$$

这很类似一个数论分块的形式,设 \(f(n, k) = \displaystyle \sum_{i=1}^{n} \mu(i) [i ⊥ k], g(n, k) = \displaystyle \sum_{i=1}^{n} [i ⊥ k]\)

那么 \(\text{Ans} = \displaystyle \sum_{d=1}^{\min(n,m)} \mu(d) [d ⊥ k] \lfloor \frac{n}{d} \rfloor g(\lfloor \frac{m}{d} \rfloor, k)\)

只要能快速在关键点处 (约为 \(\sqrt{n}\) 级别)的前缀和 (假设 \(O(a)\)),那么就可以 \(O(a\sqrt{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} \rfloor, k)\)

  • \(g(n, k) = g(n, k / p) + g(\lfloor \frac{n}{p} \rfloor, 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\n", 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\n", ans);
	}
}
posted @ 2020-12-02 17:53  DMoRanSky  阅读(190)  评论(0编辑  收藏  举报