ICPC2021台北 Gym103443E Composition with…… 解稀疏方程组

Problem – E – Codeforces

题意:有一个指定大小的区域,现在需要在这个区域里放一些照片,每张照片之间和最外面一圈都必须有一个宽为 $1$ 的边框。

照片可以进行放缩,只要长宽比不变即可。输入是递归给出的,一个子区域可以是单张照片,也可以横向或竖向划分成若干个更小的区域。

判断是否有解,如果有解则输出方案。坐标范围 $10^3$,子区域和照片的个数加起来不超过 $2\times 10^3$。


考虑把每个子区域或者照片的长和宽分别设为一个未知量,那么照片的长和宽成比例是一个等式,子区域划分的一维长度加起来等于区间长度是一个等式,没有划分的一维依次相等也是一个等式。

假设总共有 $n$ 个子区域或者照片,那么就得到了有 $2n$ 个未知量和 $2n – 1$ 个等式的方程组。再加上总体的宽度固定这一个等式,就得到了一个 $2n \times 2n$ 的满秩(显然)方程组。

如果能解出这个方程组,判断一下总体的高度是否和给定的恰好相符,并且每个未知量的值都是整数,就说明是有解的,否则无解。

解稀疏方程组有一种方法是用 Berlekamp-Massey 求最小递推式,不过因为它的精度很差,可以在模意义下做。显然方程组中非零系数只有 $O(n)$ 个,所以复杂度是 $O(n^2)$。(Berlekamp-Massey 的各种用法属实有点牛逼,我在考虑要不要单发一篇这个算法的应用。)

注意瓶颈都在模意义乘法上,所以大部分运算都是 $64$ 位整数运算,所以才会有在 CF 上交 $64$ 位能过,而 $32$ 位刚好过不了的情况。

#include <bits/stdc++.h>

using namespace std;

constexpr int maxn = 4005, p = 1004535809;

int qpow(int a, int b) {
	int ans = 1;
	while (b) {
		if (b & 1)
			ans = (long long)ans * a % p;
		
		b >>= 1;
		a = (long long)a * a % p;
	}
	return ans;
}

vector<int> berlekamp_massey(const vector<int> &a) {
	vector<int> v, last; // v is the answer, 0-based
	int k = -1, delta = 0;

	for (int i = 0; i < (int)a.size(); i++) {

		int tmp = 0;
		for (int j = 0; j < (int)v.size(); j++)
			tmp = (tmp + (long long)a[i - j - 1] * v[j]) % p;
		
		if (a[i] == tmp)
			continue;
		
		if (k < 0) {
			k = i;
			delta = (a[i] - tmp + p) % p;
			v = vector<int>(i + 1);

			continue;
		}

		vector<int> u = v;
		int val = (long long)(a[i] - tmp + p) * qpow(delta, p - 2) % p;

		if (v.size() < last.size() + i - k)
			v.resize(last.size() + i - k);
		
		(v[i - k - 1] += val) %= p;
		
		for (int j = 0; j < (int)last.size(); j++) {
			v[i - k + j] = (v[i - k + j] - (long long)val * last[j]) % p;
			if (v[i - k + j] < 0)
				v[i - k + j] += p;
		}

		if ((int)u.size() - i < (int)last.size() - k) {
			last = u;
			k = i;
			delta = a[i] - tmp;
			if (delta < 0)
				delta += p;
		}
	}

	for (auto &x : v) // 一般是需要最小递推式的, 所以处理一下
		x = (p - x) % p;
	v.insert(v.begin(), 1);

	return v; // $\forall i, \sum_{j = 0} ^ m a_{i - j} v_j = 0$
}

vector<int> solve_sparse_equations(const vector<tuple<int, int, int> > &A, const vector<int> &b) {
	int n = (int)b.size(); // 0-based

	vector<vector<int> > f({b});

	for (int i = 1; i < 2 * n; i++) {
		vector<int> v(n);
		auto &u = f.back();

		for (auto [x, y, z] : A)// [x, y, value]
			v[x] = (v[x] + (long long)u[y] * z) % p;

		f.push_back(v);
	}

	vector<int> w(n);
	mt19937 gen;
	for (auto &x : w)
		x = gen() % (p - 1) + 1;

	vector<int> a(2 * n);
	for (int i = 0; i < 2 * n; i++)
		for (int j = 0; j < n; j++)
			a[i] = (a[i] + (long long)f[i][j] * w[j]) % p;
	
	auto c = berlekamp_massey(a);
	int m = (int)c.size();

	vector<int> ans(n);

	for (int i = 0; i < m - 1; i++)
		for (int j = 0; j < n; j++)
			ans[j] = (ans[j] + (long long)c[m - 2 - i] * f[i][j]) % p;

	int inv = qpow(p - c[m - 1], p - 2);

	for (int i = 0; i < n; i++)
		ans[i] = (long long)ans[i] * inv % p;

	return ans;
}

vector<int> ch[maxn];

int tp[maxn], hor[maxn], ver[maxn]; // 0: horizonal  1: vertical  -1 : photo
int cnt;

char a[1005][1005];

int build(int t) {
	int k;
	scanf("%d", &k);

	int o = ++cnt;

	if (!k) {
		scanf("%d%d", &hor[o], &ver[o]);
		tp[o] = -1;
	}
	else {
		tp[o] = t;
		while (k--)
			ch[o].push_back(build(t ^ 1));
	}

	return o;
}

void dfs(int o, int lx, int ly, int rx, int ry) { // x is vertical
	for (int i = ly; i <= ry; i++)
		a[lx][i] = a[rx][i] = '*';
	
	for (int i = lx + 1; i < rx; i++)
		a[i][ly] = a[i][ry] = '*';
	
	if (tp[o] == 0) {
		int y = ly;
		for (int u : ch[o]) {
			dfs(u, lx, y, rx, y + hor[u] + 1);
			y += hor[u] + 1;
		}
	}
	else if (tp[o] == 1) {
		int x = lx;
		for (int u : ch[o]) {
			dfs(u, x, ly, x + ver[u] + 1, ry);
			x += ver[u] + 1;
		}
	}
}

int main() {

	int H, V;
	scanf("%d%d", &H, &V);

	build(0);

	int n = cnt; // 2n variables

	if (H <= 2 || V <= 2) {
		printf("Impossible\n");
		return 0;
	}

	int id = 0;

	vector<tuple<int, int, int> > A;
	vector<int> b;
	
	for (int i = 1; i <= n; i++) {
		if (tp[i] == -1) {
			A.emplace_back(id, i - 1, ver[i]);
			A.emplace_back(id, i + n - 1, p - hor[i]);
			b.push_back(0);
			id++;
		}
		else if (tp[i] == 0) {
			for (int x : ch[i]) {
				A.emplace_back(id, i + n - 1, 1);
				A.emplace_back(id, x + n - 1, p - 1);
				b.push_back(0);
				id++;
			}

			A.emplace_back(id, i - 1, 1);
			for (int x : ch[i])
				A.emplace_back(id, x - 1, p - 1);
			b.push_back((int)ch[i].size() - 1);
			id++;
		}
		else {
			for (int x : ch[i]) {
				A.emplace_back(id, i - 1, 1);
				A.emplace_back(id, x - 1, p - 1);
				b.push_back(0);
				id++;
			}

			A.emplace_back(id, i + n - 1, 1);
			for (int x : ch[i])
				A.emplace_back(id, x + n - 1, p - 1);
			b.push_back((int)ch[i].size() - 1);
			id++;
		}
	}

	A.emplace_back(id, 0, 1);
	b.push_back(H - 2);
	
	assert(++id == n * 2);

	auto x = solve_sparse_equations(A, b);

	bool bad = false;

	for (int i = 1; i <= n; i++) {
		hor[i] = x[i - 1];
		ver[i] = x[i + n - 1];

		if (hor[i] > H || ver[i] > V || !hor[i] || !ver[i])
			bad = true;
	}
	
	bad |= (ver[1] != V - 2);

	if (bad)
		printf("Impossible\n");
	else {
		dfs(1, 1, 1, V, H);

		for (int i = 1; i <= V; i++) {
			for (int j = 1; j <= H; j++)
				putchar(a[i][j] ? '*' : ' ');
			
			putchar('\n');
		}
	}

	return 0;
}
暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇