【USACO】USACO 2020 US Open Contest

Problem A. Sprinklers 2: Return of the Alfalfa

若干包含右上或是左下角的矩形的并是一条单调向右、或向下的轮廓线。

考虑枚举最终两种作物的分界线,则不难发现,分界线的拐角处必须放置指定装置,其余位置可以不放置装置,也可以放置其中一种装置。因此,可以认为,轮廓线每拐一次弯,该轮廓线的贡献变为 1 2 \frac{1}{2} 21 ,从而进行动态规划计算答案。

时间复杂度 O ( N 2 ) O(N^2) O(N2)

#include<bits/stdc++.h>
using namespace std;
const int MAXN = 2005;
const int P = 1e9 + 7;
const int inv = (P + 1) / 2;
typedef long long ll;
template <typename T> void chkmax(T &x, T y) {x = max(x, y); }
template <typename T> void chkmin(T &x, T y) {x = min(x, y); } 
template <typename T> void read(T &x) {
	x = 0; int f = 1;
	char c = getchar();
	for (; !isdigit(c); c = getchar()) if (c == '-') f = -f;
	for (; isdigit(c); c = getchar()) x = x * 10 + c - '0';
	x *= f;
}
int power(int x, int y) {
	if (y == 0) return 1;
	int tmp = power(x, y / 2);
	if (y % 2 == 0) return 1ll * tmp * tmp % P;
	else return 1ll * tmp * tmp % P * x % P;
}
void update(int &x, int y) {
	x += y;
	if (x >= P) x -= P;
}
char s[MAXN][MAXN];
int n, dp[MAXN][MAXN][2];
int main() {
	freopen("sprinklers2.in", "r", stdin);
	freopen("sprinklers2.out", "w", stdout);
	read(n); int cnt = 0;
	for (int i = 1; i <= n; i++) {
		scanf("\n%s", s[i] + 1);
		for (int j = 1; j <= n; j++)
			cnt += s[i][j] == '.';
	}
	dp[1][0][0] = dp[0][1][1] = 1;
	for (int i = 0; i <= n; i++)
	for (int j = 0; j <= n; j++) {
		if (dp[i][j][0]) {
			int tmp = dp[i][j][0];
			if (i < n) update(dp[i + 1][j][0], tmp);
			if (j < n && s[i][j + 1] == '.') update(dp[i][j + 1][1], 1ll * tmp * inv % P);
		}
		if (dp[i][j][1]) {
			int tmp = dp[i][j][1];
			if (j < n) update(dp[i][j + 1][1], tmp);
			if (i < n && s[i + 1][j] == '.') update(dp[i + 1][j][0], 1ll * tmp * inv % P);
		}
	}
	cout << 1ll * power(2, cnt) * (dp[n][n][0] + dp[n][n][1]) % P << endl;
	return 0;
}

Problem B. Exercise

考虑枚举质数 p p p ,指数 q q q ,记 f ( x ) f(x) f(x) 表示存在一个环长是 x x x 的倍数的置换个数。
则可以认为, p q p^q pq 对答案的贡献为
× p f ( p q ) \times p^{f(p^q)} ×pf(pq)

考虑在模 φ ( M ) = M − 1 \varphi(M)=M-1 φ(M)=M1 意义下计算 f ( x ) f(x) f(x)

显然有动态规划解法,单次计算时间复杂度 O ( N 2 ) O(N^2) O(N2) ,总时间复杂度 O ( N 3 ) O(N^3) O(N3)

#include<bits/stdc++.h>
using namespace std;
const int MAXN = 3005;
typedef long long ll;
template <typename T> void chkmax(T &x, T y) {x = max(x, y); }
template <typename T> void chkmin(T &x, T y) {x = min(x, y); } 
template <typename T> void read(T &x) {
	x = 0; int f = 1;
	char c = getchar();
	for (; !isdigit(c); c = getchar()) if (c == '-') f = -f;
	for (; isdigit(c); c = getchar()) x = x * 10 + c - '0';
	x *= f;
}
bool key[MAXN];
int P, fac[MAXN], binom[MAXN][MAXN];
int tot, prime[MAXN], f[MAXN];
void sieve(int n) {
	fac[0] = binom[0][0] = 1;
	for (int i = 1; i <= n; i++) {
		binom[i][0] = 1;
		fac[i] = 1ll * fac[i - 1] * i % P;
		for (int j = 1; j <= i; j++)
			binom[i][j] = (binom[i - 1][j - 1] + binom[i - 1][j]) % P;
	}
	for (int i = 2; i <= n; i++) {
		if (f[i] == 0) {
			prime[++tot] = f[i] = i;
			key[i] = true;
		} else key[i] = key[i / f[i]] && (f[i / f[i]] == f[i]);
		for (int j = 1; j <= tot && prime[j] <= f[i]; j++) {
			int tmp = prime[j] * i;
			if (tmp > n) break;
			f[tmp] = prime[j];
		}
	}
}
int n, dp[MAXN][2];
void update(int &x, int y) {
	x += y;
	if (x >= P) x -= P;
}
int power(int x, int y) {
	if (y == 0) return 1;
	int tmp = power(x, y / 2);
	if (y % 2 == 0) return 1ll * tmp * tmp % (P + 1);
	else return 1ll * tmp * tmp % (P + 1) * x % (P + 1);
}
int calc(int x) {
	memset(dp, 0, sizeof(dp)), dp[n][0] = 1;
	for (int i = n; i >= 1; i--)
	for (int j = 1; j <= i; j++) {
		int coef = 1ll * binom[i - 1][j - 1] * fac[j - 1] % P;
		if (j % x == 0) {
			update(dp[i - j][1], 1ll * dp[i][0] * coef % P);
			update(dp[i - j][1], 1ll * dp[i][1] * coef % P);
		} else {
			update(dp[i - j][0], 1ll * dp[i][0] * coef % P);
			update(dp[i - j][1], 1ll * dp[i][1] * coef % P);
		}
	}
	cout << dp[0][1] << endl;
	return dp[0][1];
}
int main() {
	freopen("exercise.in", "r", stdin);
	freopen("force.out", "w", stdout);
	read(n), read(P), P--;
	sieve(n); int ans = 1;
	for (int i = 1; i <= n; i++)
		if (key[i]) ans = 1ll * ans * power(f[i], calc(i)) % (P + 1);
	cout << ans << endl;
	return 0;
}

观察上述动态规划的转移形式及其转移系数,可以将其优化至单次 O ( N ) O(N) O(N)

时间复杂度 O (

  • 3
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值