题解:P11132 【MX-X5-T4】「GFOI Round 1」epitaxy

1.做法及证明

因为 \(n\) 一定会被包含在某一区间内,所以最后答案肯定是 \(n\) 的因数。

先给出结论:对于 \(n\) 的因数 \(d\),其合法的充要条件为 \(d\le m\),所以我们只需要找到第一个小于等于 \(m\)\(d\) 即可。

接下来我们来证明。

下文用 \(i'\) 表示以第 \(i\) 位开头的长度为 \(m\) 的区间,\(p_i\) 同题意。

对于一个值 \(p_i\),其能影响到的 \(i'\) 为所有的 \(j',j\in[i-m+1,i]\)。我们称其管辖着区间 \([i-m+1,i]\)。当然越过边界的自动将左边界变为 \(1\)

\(kd\) 表示最小的成为所管辖区间最大值的值。

显然的,我们需要由 \(d\) 的倍数来作为所有 \(i'\) 的最大值,而一个数可管辖的区间显然长度不超过 \(m\),如果 \(d>m\),那么就说明 \(kd\) 管辖的区间无法填入所有大于 \((k-1)d\) 的数,那么由 \((k-1)d\) 管辖的区间就一定会与某个大于其的数所管辖区间相交,那么一定存在 \(i'\) 的最大值不为 d 的倍数,不合法。

相反的,如果 \(d\le m\) 就一定不存在 \(i'\) 的最大值不为 d 的倍数,因为所有大于 \((k-1)d\) 的数都被填掉了。

知道以上几点就很好构造了,只要在 \(m\) 的倍数的位置,从大到小填入 \(d\) 的倍数,接着在空位从大到小填入剩下没填的数即可。

2.Code:

因为写的时候很难受,所以很丑,轻喷。

#include <bits/stdc++.h>
#define ll long long
#define ull unsigned long long
#define m_p make_pair
#define m_t make_tuple
#define N 1000010
using namespace std;
int a[N];
bitset<N> vis;
signed main()
{
	ios::sync_with_stdio(false);
	cin.tie(nullptr);
	cout.tie(nullptr);
	int T;
	int n, m, qn, d, x, fl, num;
	vector<int> p;
	cin >> T;
	while (T--)
	{
		cin >> n >> m;
		p.clear();
		qn = sqrt(n);
		for (int i = 1; i <= n; i++)
			a[i] = vis[i] = 0;
		for (int i = 1; i < qn; i++)
		{
			if (n % i)
				continue;
			p.push_back(i);
			p.push_back(n / i);
		}
		if (qn * qn == n)
			p.push_back(qn);
		else if (n % qn == 0)
		{
			p.push_back(qn);
			p.push_back(n / qn);
		}
		sort(p.begin(), p.end());
		for (int i = p.size() - 1; i >= 0; i--)
		{
			d = p[i];
			if (d <= m)
				break;
		}
		x = n;
		for (int i = m; i <= n; i += m)
		{
			a[i] = x;
			vis[x] = 1;
			x -= d;
		}
		x = n;
		for (int i = 1; i <= n; i++)
		{
			if (a[i])
				cout << a[i] << " ";
			else
			{
				while (vis[x])
					--x;
				cout << x << " ";
				--x;
			}
		}
		cout << "\n";
	}
	return 0;
}

人生中距离 AK 最近的一次,结果我没有把握好。

作为 J 组的最后一题,如果我当时写了对拍,或许结果就会不一样了。

posted @ 2024-09-29 22:39  -wryyy-  阅读(18)  评论(0编辑  收藏  举报