一道题

image


这是学弟问我的一道题, 竟然是以前cf的题, 我大概想对了一多半吧, 如果和我队友一起的话应该是能够A掉的。。 首先两个数相乘是平方数, 那么把这个数质因数分解的话, 每个因数的幂一定是2的倍数, 假设一个因子是(a^{2i+1}), 那我们不妨看成(a^1), 当然对答案是没有影响的, 那么问题就变成, 分组后每组不能有相同的数字,那么我们就想到dp的做法。 设dp[i][j]表示前i个数, 改变j次最小的分组数。当然我们需要知道, 每个数前面改变i次所能达到的左端点, 我们记为l[i][j], 这个可以用双指针(O(nk))求出。转移方程的话就很简单。
(dp[i][j] = min(dp[i][j], dp[l[i][q] - 1][j - q] + 1) (q≤j))

点击查看代码
#include <bits/stdc++.h>

using namespace std;

const int INF = 0x3f3f3f3f;
const int N = 1e5 + 10;
const int M = 1e7 + 10;
const int maxn = 3e3;

template < typename T > inline void read(T &x) {
	x = 0; T ff = 1, ch = getchar();
	while (!isdigit(ch)) {
		if (ch == '-') ff = -1;
		ch = getchar();
	}
	while (isdigit(ch)) {
		x = (x << 1) + (x << 3) + (ch ^ 48);
		ch = getchar();
	}
	x *= ff;
}

vector < int > v;
int n, k, a[N], dp[N][21], l[N][21];
int cnt[M], vis[M];

inline void pre() {
	for (int i = 2; i <= maxn; ++i) {
		if (!vis[i]) {
			vis[i] = i;
			v.push_back(i);
		}
		for (int j = 0; j < v.size(); ++j) {
			if (v[j] > vis[i] || v[j] > maxn / i) break;
			vis[i * v[j]] = v[j];
		}
	}
}

inline void work() {
	for (int i = 1; i <= n; ++i) {
		int cnt = 1;		
		for (int j = 0; j < v.size(); ++j) {
			int y = v[j], w = 0;
			while (a[i] % y == 0) a[i] /= y, ++w;
			if (w & 1) cnt *= y;
			if (a[i] == 1) break;
		}
		a[i] *= cnt;
	}
}

int main() {
//	freopen("team.in", "r", stdin);
//	freopen("team.out", "w", stdout);
	read(n), read(k);
	for (int i = 1; i <= n; ++i) read(a[i]);
	pre();
	work();
	for (int i = 0; i <= k; ++i) {
		int L = 1, sum = 0;
		for (int j = 1; j <= n; ++j) {
			++cnt[a[j]];
			if (cnt[a[j]] > 1) ++sum;
			while (sum > i) {
				--cnt[a[L]];
				if (cnt[a[L]]) --sum;
				++L;
			}
			l[j][i] = L;
		} 
		while (L <= n) --cnt[a[L]], ++L; 
	}
	memset(dp, 0x3f, sizeof(dp));
	for (int i = 0; i <= k; ++i) dp[0][i] = 0;
	for (int i = 1; i <= n; ++i) 
		for (int j = 0; j <= k; ++j) 
			for (int q = 0; q <= j; ++q) 
				dp[i][j] = min(dp[i][j], dp[l[i][q] - 1][j - q] + 1);
	int ans = INF;
	for (int i = 0; i <= k; ++i) ans = min(ans, dp[n][i]);	
	printf("%d
", ans);
	return 0;
}

原文地址:https://www.cnblogs.com/AK-ls/p/15515342.html