列表

详情


NC221010. 同源数组(HardVersion)

描述

本题有Easy,Hard两个版本,两个版本之间的区别仅在数据范围,通过Hard Version的代码可以直接通过Easy Version。保证Easy Version的测试用例集是Hard Version的子集

智乃酱最近学习了前缀和、差分。她现在对于这两个操作产生了浓厚的兴趣。

具体来说,前缀和是这样一种操作,假设数组是数组的前缀和数组,则有

而差分的操作则是前缀和反过来,假设数组是数组的差分数组,则有

我们将先求出数组,再把中的值赋值回称为对数组做一次前缀和,同理,将先求出数组,再把中的值赋值回数组称之为一次差分。

如果两个长度相同的数组可以通过对数组或者数组做若干次前缀和或者差分,使两个数组中所有元素在mod (p是一个素数)下是相等的,则称两个数组同源。
显然易得,如果存在三个长度相同的数组,如果同源且同源,则也是同源的。

我们把一些两两之间互相同源的数组放到一起,它们就称为一个“同源集合”。

现在给你n个长度大小均为m的数组,你需要将它们划分成若干个同源集合,要求你划分的集合数目最少。
请输出一个合理的划分方案。

输入描述

第一行输入三个正整数并且保证p是一个质数。
接下来n行,每行m个整数表示第i个数组的第j个元素

输出描述

本题为spj,你可以输出任何符合题目要求的答案。
首先输出一个正整数g表示划分的同源集合数目。
接下来按照顺序输出每个集合中的数组编号。(数组从0开始编号)
输出行,首先输出一个整数,表示集合的大小,接下来输出一行若干整数,整数与整数之间空一个空格。

示例1

输入:

15 5 998244353
0 0 0 0 5
1 1 1 1 1
0 0 1 1 1
1 3 6 10 15
0 0 0 7 7
1 0 0 0 0
0 0 0 0 6
0 0 0 7 10
0 0 0 9 8
1 2 1 2 1
0 0 0 0 5
0 0 1 2 1
1 2 3 4 5
0 0 0 9 2
0 0 0 7 10086

输出:

8
4
5 1 12 3
1
9
1
2
1
11
3
4 7 14
2
13 8
2
0 10
1
6

示例2

输入:

6 8 7
4 4 6 4 0 2 1 1
4 2 0 2 1 6 4 1
4 5 5 2 6 5 5 4
4 4 6 4 0 2 1 1
4 5 5 2 6 5 5 1
4 2 0 2 1 6 4 1

输出:

1
6
0 1 2 3 4 5

原站题解

上次编辑到这里,代码来自缓存 点击恢复默认模板

C++ 解法, 执行用时: 1267ms, 内存消耗: 3256K, 提交时间: 2021-10-15 21:34:37

#pragma GCC optimize("3", "inline", "Ofast")
#include<bits/stdc++.h>
using namespace std;
   
#define broken fprintf(stderr, "running on %s %d\n", __FUNCTION__, __LINE__)
#define sz(a) int((a).size())
#define x first
#define y second
#define mp make_pair
typedef long long i64;
typedef unsigned long long u64;
const int N = 1e2 + 10, M = 1e3 + 10, T = 1e5 + 10, V = T - 10;
vector<int> a[N];

int inv[T], fac[T], ifac[T], n, m, p;
map<vector<int>, vector<int> > g;

void Mod(int &x) { x += x >> 31 & p; }

int power(int a, int b, int c = 1) {
	for(; b; b >>= 1, a = 1ll * a * a % p)
		if(b & 1)
			c = 1ll * c * a % p;
	return c;
}

int C(int n, int m) {return n < m ? 0 : 1ll * fac[n] * ifac[m] % p * ifac[n - m] % p; }

int binom(int n, int m) {
	if(n < m) return 0;
	if(m == 0) return 1;
	int res = binom(n / p, m / p);
	n %= p, m %= p;
	if(n < m) return 0;
	if(n <= V) return 1ll * C(n, m) * res % p;
	for(int i = n; i >= n - m + 1; i--) res = 1ll * res * i % p;
	return 1ll * res * ifac[m] % p;
}

void initmath(int n) {
	fac[0] = ifac[0] = inv[1] = 1;
	for(int i = 2; i <= n; i++) inv[i] = 1ll * inv[p % i] * (p - p / i) % p;
	for(int i = 1; i <= n; i++) {
		fac[i] = 1ll * fac[i - 1] * i % p;
		ifac[i] = 1ll * ifac[i - 1] * inv[i] % p;
	}
	return;
}

int main() {
	scanf("%d%d%d", &n, &m, &p);
	initmath(V);
	for(int i = 0; i < n; i++) {
		a[i].resize(m);
		for(int j = 0; j < m; j++) {
			scanf("%d", &a[i][j]);
		}
		vector<int> b(m);
		int pos = 0;
		while(pos < m && !a[i][pos]) pos++;
		if(pos < m - 1) {
			int x = 0, base = 1;
			b = a[i];
			for(int j = 1; pos + j < m; j++) {
				int t = j;
				for(; t % p == 0; t /= p);
				if(t == 1) {
					x += 1ll * b[pos + j] * power(a[i][pos], p - 2) % p * base;
					base *= p;
				}
				int coef = binom(x, j);
				if(j & 1) coef = (p - coef) % p;
				for(int k = pos; k + j < m; k++) {
					Mod(b[k + j] += 1ll * coef * a[i][k] % p - p);
				}
			}
		} else {
			b = a[i];
		}
		g[b].push_back(i);
	}
	printf("%d\n", sz(g));
	for(auto v : g) {
		printf("%d\n", sz(v.y));
		for(int a : v.y) printf("%d ", a);
		printf("\n");
	}
	return 0;
}

上一题