ABC251 Ex Fill Triangle [题解]

Fill Triangle

题意

给定 \(M\) 个二元组 \((a_i, c_i)\),序列 \(A\)\(N\) 个元素组成,被表示为了这 \(M\) 个二元组。

二元组 \((a_i, c_i)\) 表示连续 \(c_i\)\(a_i\)

例如: \(A = (2, 2, 2, 5, 5, 1)\) 可以被表示为 \(((2, 3), (5, 2), (1, 1))\)

你将生成一个 \(B\) 三角形, 其中:

  • \(1\leq i\leq N\)\(B_{N, i} = A_i\)

  • \(1\leq j\leq i\leq N - 1\)\(B_{i,j} = (B_{i + 1, j} + B_{i + 1, j + 1})\) \(mod\) \(7\)

输出三角形第 \(K\) 排。

\(1\leq N\leq 10^9\)
\(1\leq M\leq min(N, 200)\)
\(1\leq K\leq min(N, 5\times 10 ^ 5)\)
\(0\leq a_i\leq 6\)
\(1\leq c_i\leq N\)
\(\sum_{i = 1}^M c_i = N\)

分析

整理手中的信息,我们目前只知道 \(A\),考虑如何通过 \(A\) 中的数快速表示 \(B\)

假设向上递推至 \(N - 1\) 排,则 \(B_{N - 1, 1} = 1\times A_1 + 1\times A_2\)

递推至 \(N - 2\)排,则 \(B_{N - 2, 1} = 1\times A_1 + 2\times A_2 + 1\times A_3\)

以此类推,不难发现其系数杨辉三角中的某一排。且 \(B\) 的每一排即系数不变在 \(A\) 数组中从左向右平移

很自然的,由于 \(M\) 很小,我们想通过处理出一排杨辉三角的前缀和来快速求解。

很显然暴力求解是不可行的,我们需要的是杨辉三角中第 \(n - k + 1\) 排,大小是 \(10^9\) 级别。注意到很小的模数 \(7\),感觉上这个地方应该会有一些性质,考虑将对 \(7\) 取模数的杨辉三角输出出来观察。

1
1 1
1 2 1
1 3 3 1
1 4 6 4 1
1 5 3 3 5 1
1 6 1 6 1 6 1
1
1 1
1 2 1
1 3 3 1
1 4 6 4 1
1 5 3 3 5 1
1 6 1 6 1 6 1

1 0 0 0 0 0 0 1
1 1 0 0 0 0 0 1 1
1 2 1 0 0 0 0 1 2 1
1 3 3 1 0 0 0 1 3 3 1
1 4 6 4 1 0 0 1 4 6 4 1
1 5 3 3 5 1 0 1 5 3 3 5 1
1 6 1 6 1 6 1 1 6 1 6 1 6 1

1 0 0 0 0 0 0 2 0 0 0 0 0 0 1
1 1 0 0 0 0 0 2 2 0 0 0 0 0 1 1
1 2 1 0 0 0 0 2 4 2 0 0 0 0 1 2 1
1 3 3 1 0 0 0 2 6 6 2 0 0 0 1 3 3 1
1 4 6 4 1 0 0 2 1 5 1 2 0 0 1 4 6 4 1
1 5 3 3 5 1 0 2 3 6 6 3 2 0 1 5 3 3 5 1
1 6 1 6 1 6 1 2 5 2 5 2 5 2 1 6 1 6 1 6 1

1 0 0 0 0 0 0 3 0 0 0 0 0 0 3 0 0 0 0 0 0 1
1 1 0 0 0 0 0 3 3 0 0 0 0 0 3 3 0 0 0 0 0 1 1
1 2 1 0 0 0 0 3 6 3 0 0 0 0 3 6 3 0 0 0 0 1 2 1
1 3 3 1 0 0 0 3 2 2 3 0 0 0 3 2 2 3 0 0 0 1 3 3 1
1 4 6 4 1 0 0 3 5 4 5 3 0 0 3 5 4 5 3 0 0 1 4 6 4 1
1 5 3 3 5 1 0 3 1 2 2 1 3 0 3 1 2 2 1 3 0 1 5 3 3 5 1
1 6 1 6 1 6 1 3 4 3 4 3 4 3 3 4 3 4 3 4 3 1 6 1 6 1 6 1

1 0 0 0 0 0 0 4 0 0 0 0 0 0 6 0 0 0 0 0 0 4 0 0 0 0 0 0 1
1 1 0 0 0 0 0 4 4 0 0 0 0 0 6 6 0 0 0 0 0 4 4 0 0 0 0 0 1 1
1 2 1 0 0 0 0 4 1 4 0 0 0 0 6 5 6 0 0 0 0 4 1 4 0 0 0 0 1 2 1
1 3 3 1 0 0 0 4 5 5 4 0 0 0 6 4 4 6 0 0 0 4 5 5 4 0 0 0 1 3 3 1
1 4 6 4 1 0 0 4 2 3 2 4 0 0 6 3 1 3 6 0 0 4 2 3 2 4 0 0 1 4 6 4 1
1 5 3 3 5 1 0 4 6 5 5 6 4 0 6 2 4 4 2 6 0 4 6 5 5 6 4 0 1 5 3 3 5 1
1 6 1 6 1 6 1 4 3 4 3 4 3 4 6 1 6 1 6 1 6 4 3 4 3 4 3 4 1 6 1 6 1 6 1

1 0 0 0 0 0 0 5 0 0 0 0 0 0 3 0 0 0 0 0 0 3 0 0 0 0 0 0 5 0 0 0 0 0 0 1
1 1 0 0 0 0 0 5 5 0 0 0 0 0 3 3 0 0 0 0 0 3 3 0 0 0 0 0 5 5 0 0 0 0 0 1 1
1 2 1 0 0 0 0 5 3 5 0 0 0 0 3 6 3 0 0 0 0 3 6 3 0 0 0 0 5 3 5 0 0 0 0 1 2 1
1 3 3 1 0 0 0 5 1 1 5 0 0 0 3 2 2 3 0 0 0 3 2 2 3 0 0 0 5 1 1 5 0 0 0 1 3 3 1
1 4 6 4 1 0 0 5 6 2 6 5 0 0 3 5 4 5 3 0 0 3 5 4 5 3 0 0 5 6 2 6 5 0 0 1 4 6 4 1
1 5 3 3 5 1 0 5 4 1 1 4 5 0 3 1 2 2 1 3 0 3 1 2 2 1 3 0 5 4 1 1 4 5 0 1 5 3 3 5 1
1 6 1 6 1 6 1 5 2 5 2 5 2 5 3 4 3 4 3 4 3 3 4 3 4 3 4 3 5 2 5 2 5 2 5 1 6 1 6 1 6 1

1 0 0 0 0 0 0 6 0 0 0 0 0 0 1 0 0 0 0 0 0 6 0 0 0 0 0 0 1 0 0 0 0 0 0 6 0 0 0 0 0 0 1
1 1 0 0 0 0 0 6 6 0 0 0 0 0 1 1 0 0 0 0 0 6 6 0 0 0 0 0 1 1 0 0 0 0 0 6 6 0 0 0 0 0 1 1
1 2 1 0 0 0 0 6 5 6 0 0 0 0 1 2 1 0 0 0 0 6 5 6 0 0 0 0 1 2 1 0 0 0 0 6 5 6 0 0 0 0 1 2 1
1 3 3 1 0 0 0 6 4 4 6 0 0 0 1 3 3 1 0 0 0 6 4 4 6 0 0 0 1 3 3 1 0 0 0 6 4 4 6 0 0 0 1 3 3 1
1 4 6 4 1 0 0 6 3 1 3 6 0 0 1 4 6 4 1 0 0 6 3 1 3 6 0 0 1 4 6 4 1 0 0 6 3 1 3 6 0 0 1 4 6 4 1
1 5 3 3 5 1 0 6 2 4 4 2 6 0 1 5 3 3 5 1 0 6 2 4 4 2 6 0 1 5 3 3 5 1 0 6 2 4 4 2 6 0 1 5 3 3 5 1
1 6 1 6 1 6 1 6 1 6 1 6 1 6 1 6 1 6 1 6 1 6 1 6 1 6 1 6 1 6 1 6 1 6 1 6 1 6 1 6 1 6 1 6 1 6 1 6 1

我们发现,若我们将如上所示前 \(7\) 排的杨辉三角形称为 \(1\) 号三角形,将前 \(7^i\) 排三角形成为 \(i\) 号三角形。那么,第 \(i(i\ge 2)\) 号三角形可以被第 \(i-1\) 号三角形表示。具体的,将 \(i-1\) 号三角形看作一个元,第 \(i\) 号三角形即为这个元替换进一号三角形(相当于一行三角形中的数为系数)得到的大三角形。

考虑通过这个,我们如何计算第 \(i\) 排杨辉三角的前缀。

设计一个函数 \(Get_sum(id, x, y)\) 表示在以 \(id\) 号三角形为元的大三角形中,求第 \(x\)\(1\)\(y\) 的总和。首先,我们可以确定我们当前求解的排属于整个大三角形中的哪一排,求解这个的原因是因为我们需要知道这一层的系数。之后,我们将该排的元展开,进入下一层,递归求解。需要注意的是边界条件。

具体的,例如刚好是 \(id\) 号三角形的最后一排,\(y\) 不再是 \(id\) 号三角形完整的一排等。

这个递归是 \(log_7\) 的,对于 \(10^9\) 来说,最多需要十层递归。最后的时间复杂度是 \(O(10km)\),遗憾的是,并不能通过此题。

考虑如何优化,首先,我们可以将 \(sum[i]\) 即对于 \(m\) 个二元组来说的 \(m\) 个节点对应的值预处理出来,之后可以通过差值直接计算,于是我们就拿掉了常数 \(10\),但 \(sum[i]\) 并非一成不变,我们系数对应的数需要左移。简单的,我们只需要知道每一次移动边界上的杨辉三角元素的大小就行了。想到了 \(Lucas\) 定理可以快速求解,但又添了一个 \(log_7\),幸运的是,\(Lucas\) 定理求解需要用到的数我们也能通过预处理得到,这样,时间复杂度就被优化到了 \(O(km)\),足以通过此题。

#include <bits/stdc++.h>
#define MP make_pair
using namespace std;
const int N = 2e2 + 10, lim = 10, mod = 7;
const int s[8] = {0, 1, 2, 4, 1, 2, 4, 1};
inline int read()
{
	int s = 0, w = 1;
	char ch = getchar();
	while(ch < '0' || ch > '9') { if(ch == '-') w *= -1; ch = getchar(); }
	while(ch >= '0' && ch <= '9') s = s * 10 + ch - '0', ch = getchar();
	return s * w;
}
struct node{
	int a, c;
}arr[N];
int n, m, k;
int cs[N], sum[N];
int C[10][10], S[N][N];
int T = 117649, f1[120000], f2[120000];
int fac[N]; //fac[i] 表示 7 ^ i 次方 
/*
	以三角形:
	1              sum = 1 
	1 1            sum = 2
	1 2 1          sum = 4
	1 3 3 1        sum = 8
	1 4 6 4 1      sum = 16
	1 5 3 3 5 1    sum = 18
	1 6 1 6 1 6 1  sum = 22
	为 1 号三角形,其余三角形都是在此基础上累计得到
*/ 
inline void initial()
{
	for(int i = 0; i <= 9; i++){
		C[i][0] = 1;
		for(int j = 1; j <= i; j++)
			C[i][j] = (C[i - 1][j - 1] + C[i - 1][j]) % mod;
	}
	for(int i = 0; i <= 9; i++){
		S[i][0] = 1;
		for(int j = 1; j <= 9; j++)
			S[i][j] = (S[i][j - 1] + C[i][j]) % mod;
	}
	fac[0] = 1;
	for(int i = 1; i <= lim; i++) fac[i] = fac[i - 1] * 7; 
}
inline int Get_sum(int id, int x, int y) //以 id 号构成的三角形中第 x 行 1~y 列求和 
{
	int res = 0, z = x / fac[id];
	if(!id){ //没有再小的三角形,直接计算 
		return S[x - 1][y - 1];
	}
	if(x % fac[id]) z++; //z 表示在大的三角形中处于第几行 
	int to = !(x % fac[id]) ? fac[id] : x % fac[id]; //接下来要进入的小的三角形中的第几行 
	int v = Get_sum(id - 1, to, fac[id]);
	int t = y / fac[id]; y %= fac[id];
	if(t) res = (res + S[z - 1][t - 1] * v % mod) % mod;
	if(y) res = (res + C[z - 1][t] * Get_sum(id - 1, to, y) % mod) % mod;
	return res;
}
inline int Sol(int x, int y) //求第 x 排 1 ~ y 列的系数和 
{
	for(int i = lim; i >= 0; i--)
		if(x >= fac[i]) return Get_sum(i, x, y);
}
inline int Lucas(int x, int y)
{
	if(y < 0 || y > x) return 0;
	if(x < 7) return C[x][y];
	return Lucas(x / 7, y / 7) * C[x % 7][y % 7] % 7;
}
int main()
{
	initial();
	n = read(), m = read(), k = read();
	for(int i = 1; i <= m; i++)
		arr[i].a = read(), arr[i].c = read();
	int lv = n - k + 1; //杨辉三角中的第几排
	//先计算第一个数
	for(int i = 0; i <= T; i++) f1[i] = Lucas((lv - 1) % T, i);
	for(int i = 0; i <= T; i++) f2[i] = Lucas((lv - 1) / T, i);
	int pre = 0;
	for(int i = 1; i <= m; i++){
		pre += arr[i].c;
		sum[i] = Sol(lv, pre), cs[i] = pre;
	}
	for(int t = 1; t <= k; t++){
		int ans = 0;
		for(int i = 1; i <= m; i++)
			ans = (ans + ((sum[i] - sum[i - 1]) % mod + mod) % mod * arr[i].a % mod) % mod; 
		cout << ans << " ";
		for(int i = 1; i <= m; i++){
			if(cs[i] > 0){
				sum[i] = (sum[i] - f1[(cs[i] - 1) % T] * f2[(cs[i] - 1) / T] % mod + mod) % mod;
				cs[i]--;
			}
		}
	}
	return 0;
}
posted @ 2022-05-16 11:17  ╰⋛⋋⊱๑落叶๑⊰⋌⋚╯  阅读(72)  评论(1编辑  收藏  举报