「题解」Luogu P4152 [WC2014]时空穿梭

P4152 [WC2014]时空穿梭

Description

多测。

给定一个 \(n\) 维空间,需要在这 \(n\) 维空间内选取 \(c\) 个共线的点,要求:

  • \(c\) 个点每维坐标均单调递增,即第 \(i + 1\) 个点的第 \(j\) 维坐标必须严格大于第 \(i\) 个点的第 \(j\) 维坐标。
  • \(i\) 维坐标是整数且在 \([1, m_i]\) 中。

Solution

先考虑有多少条直线,一条直线可由 起点 和 终点与起点的差向量 确定。

确定差向量为 \((x_1, x_2, \cdots, x_n)\) 后起点的第 \(i\) 维坐标可以在 \([1, m_i - x_i]\) 中取,共 \(\prod_{i = 1}^n (m_i - x_i)\) 种,此时终点也随之确定。

\(d = \gcd(x_1, x_2, \cdots, x_n)\),则这一条差向量上的整点,不算起点与终点共 \(d - 1\) 个(分别是 \(\left(\dfrac{x_1}{d}, \dfrac{x_2}{d}, \cdots, \dfrac{x_n}{d}\right)\)\(1\sim d - 1\) 倍)。

那么除起点与终点,在其中选 \(c - 2\) 个共有 \(\dbinom{d - 1}{c - 2}\) 种方法。

所以答案就是

\[\sum_{x_1 = 1}^{m_1} \sum_{x_2 = 1}^{m_2}\cdots\sum_{x_n = 1}^{m_n} \dbinom{\gcd\{x_i\} - 1}{c - 2} \prod_{i = 1}^n (m_i - x_i) \]

于是就可以愉快地进行反演了,设 \(m = \min\{m_i\}\)

\[\begin{aligned} ans & = \sum_{d = 1}^m \dbinom{d - 1}{c - 2} \sum_{x_1 = 1}^{m_1} \sum_{x_2 = 1}^{m_2} \cdots \sum_{x_n = 1}^{m_n} [\gcd\{x_i\} = d] \prod_{i = 1}^n (m_i - x_i) \\ & = \sum_{d = 1}^m \dbinom{d - 1}{c - 2} \sum_{x_1 = 1}^{\left\lfloor\frac{m_1}{d}\right\rfloor} \sum_{x_2 = 1}^{\left\lfloor\frac{m_2}{d}\right\rfloor} \cdots \sum_{x_n = 1}^{\left\lfloor\frac{m_n}{d}\right\rfloor} [\gcd\{x_i\} = 1] \prod_{i = 1}^n (m_i - dx_i) \\ & = \sum_{d = 1}^m \dbinom{d - 1}{c - 2} \sum_{k = 1}^{\left\lfloor\frac{m}{d}\right\rfloor} \mu(k) \sum_{x_1 = 1}^{\left\lfloor\frac{m_1}{dk}\right\rfloor} \sum_{x_2 = 1}^{\left\lfloor\frac{m_2}{dk}\right\rfloor} \cdots \sum_{x_n = 1}^{\left\lfloor\frac{m_n}{dk}\right\rfloor} \prod_{i = 1}^n (m_i - dk x_i) \\ & = \sum_{T = 1}^m \sum_{d\mid T} \dbinom{d - 1}{c - 2} \mu\left(\dfrac{T}{d}\right) \prod_{i = 1}^n \sum_{x_i = 1}^{\left\lfloor\frac{m_i}{T}\right\rfloor} (m_i - T x_i) \\ & = \sum_{T = 1}^m \sum_{d\mid T} \dbinom{d - 1}{c - 2} \mu\left(\dfrac{T}{d}\right) \prod_{i = 1}^n \left\lfloor\dfrac{m_i}{T}\right\rfloor m_i - T\dfrac{\left\lfloor\frac{m_i}{T}\right\rfloor(\left\lfloor\frac{m_i}{T}\right\rfloor + 1)}{2} \\ \end{aligned} \]

此时可以做到 \(\Omicron(Tnm)\)

首先 \(\left\lfloor\dfrac{m_i}{T}\right\rfloor\) 可以整除分块,然后退回到上一步

\[ans = \sum_{T = 1}^m \sum_{d\mid T} \dbinom{d - 1}{c - 2} \mu\left(\dfrac{T}{d}\right) \prod_{i = 1}^n \sum_{x_i = 1}^{\left\lfloor\frac{m_i}{T}\right\rfloor} (m_i - T x_i) \]

其中

\[\prod_{i = 1}^n \sum_{x_i = 1}^{\left\lfloor\frac{m_i}{T}\right\rfloor} (m_i - T x_i) \]

是关于 \(T\)\(n\) 次多项式,设系数为 \(f_i\)

再设

\[g(n) = \sum_{d\mid n} \dbinom{d - 1}{c - 2} \mu\left(\dfrac{n}{d}\right) \]

那么在整除分块中

\[\sum_{T = l}^r g(T) \sum_{i = 0}^n f_i T^i = \sum_{i = 0}^n f_i \sum_{T = l}^r g(T) T^i \]

于是我们需要预处理 \(g(n) n^i\) 的前缀和,设其为 \(S(n, c, i)\),这一步是 \(\Omicron(cnm)\) 的。

由于有 \(n\)\(m_i\),所以整除分块中共有 \(\Omicron(n\sqrt m)\) 个块,再加上每个块需要 \(\Omicron(n)\) 求和,所以这一段是 \(\Omicron(Tn^2\sqrt m)\) 的。

最后就是求出系数 \(f_i\) 了,直接模拟多项式乘法 \(\Omicron(n^2)\) 计算即可,你也可以写分治 NTT。

总时间复杂度为 \(\Omicron(cm\ln m + cnm + Tn^3\sqrt m)\)

Code

// 18 = 9 + 9 = 18.
#include <iostream>
#include <cstdio>
#include <cstring>
#define Debug(x) cout << #x << "=" << x << endl
typedef long long ll;
using namespace std;

namespace IO
{
	int len = 0;
	char buf[(1 << 20) + 1], *S, *T;
	#if ONLINE_JUDGE
		#define Getchar() (S == T ? T = (S = buf) + fread(buf, 1, (1 << 20) + 1, stdin), (S == T ? EOF : *S++) : *S++)
	#else
		#define Getchar() getchar()
	#endif
	#define re register

	int read()
	{
		re char c = Getchar();
		re int x = 0;
		while (c < '0' || c > '9')
			c = Getchar();
		while (c >= '0' && c <= '9')
			x = (x << 3) + (x << 1) + (c ^ 48), c = Getchar();
		return x;
    }
}
using IO::read;

const int MOD = 10007;
const int INV = 5004;
int add(int a, int b) {return (a + b) % MOD;}
int sub(int a, int b) {return (a - b + MOD) % MOD;}
int mul(int a, int b) {return (ll)a * b % MOD;}

const int MAXN = 15;
const int N = 11;
const int MAXC = 25;
const int C = 20;
const int MAXM = 1e5 + 5;
const int M = 1e5;

int p[MAXM], mu[MAXM], c[MAXM][MAXC], g[MAXM][MAXC], pw[MAXM][MAXN], s[MAXM][MAXC][MAXN];
bool vis[MAXM];

void pre()
{
	mu[1] = 1;
	for (int i = 2; i <= M; i++)
	{
		if (!vis[i])
		{
			p[++p[0]] = i;
			mu[i] = MOD - 1;
		}
		for (int j = 1; j <= p[0] && i * p[j] <= M; j++)
		{
			vis[i * p[j]] = true;
			if (i % p[j] == 0)
			{
				mu[i * p[j]] = 0;
				break;
			}
			mu[i * p[j]] = mul(mu[i], mu[p[j]]);
		}
	}
	
	for (int i = 0; i <= M; i++)
	{
		c[i][0] = 1;
		for (int j = 1; j <= min(i, C); j++)
		{
			c[i][j] = add(c[i - 1][j], c[i - 1][j - 1]);
		}
	}
	
	for (int i = 1; i <= M; i++)
	{
		for (int j = 1; i * j <= M; j++)
		{
			if (!mu[j])
			{
				continue;
			}
			for (int k = 2; k <= min(i + 1, C); k++)
			{
				g[i * j][k] = add(g[i * j][k], mul(c[i - 1][k - 2], mu[j]));
			}
		}
	}
	
	for (int i = 1; i <= M; i++)
	{
		pw[i][0] = 1;
		for (int j = 1; j <= N; j++)
		{
			pw[i][j] = mul(pw[i][j - 1], i);
		}
	}
	
	for (int i = 1; i <= M; i++)
	{
		for (int j = 2; j <= C; j++)
		{
			for (int k = 0; k <= N; k++)
			{
				s[i][j][k] = add(s[i - 1][j][k], mul(g[i][j], pw[i][k]));
			}
		}
	}
}

int f[MAXN];

void update(int a, int b)
{
	for (int i = N; i >= 0; i--)
	{
		f[i] = add(mul(f[i], a), mul(i ? f[i - 1] : 0, b));
	}
}

int GetSum(int c, int n, int l, int r)
{
	return sub(s[r][c][n], s[l - 1][c][n]);
}

const int INF = 0x3f3f3f3f;

int m[MAXN];

int main()
{
	pre();
	int t = read();
	while (t--)
	{
		int n = read(), c = read(), mn = INF;
		for (int i = 1; i <= n; i++)
		{
			m[i] = read();
			mn = min(mn, m[i]);
		}
		int ans = 0;
		for (int l = 1, r; l <= mn; l = r + 1)
		{
			memset(f, 0, sizeof(f));
			f[0] = 1;
			r = l;
			for (int i = 1; i <= n; i++)
			{
				int k = m[i] / l;
				r = min(r, m[i] / k);
				update(mul(k, m[i]), sub(0, mul(mul(k, k + 1), INV)));
			}
			for (int i = 0; i <= n; i++)
			{
				ans = add(ans, mul(f[i], GetSum(c, i, l, r)));
			}
		}
		printf("%d\n", ans);
	}
	return 0;
}
posted @ 2022-02-19 11:52  mango09  阅读(53)  评论(0编辑  收藏  举报
-->