【AtCoder】Tenka1 Programmer Contest 2019 题解

【比赛链接】

【题解链接】

【C】 Stones

【思路要点】

  • 枚举最后的石子分布,用前缀和优化计算代价。
  • 时间复杂度 O ( N ) O(N) O(N)

【代码】

#include<bits/stdc++.h>
using namespace std;
const int MAXN = 2e5 + 5;
typedef long long ll;
typedef long double ld;
typedef unsigned long long ull;
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;
}
template <typename T> void write(T x) {
	if (x < 0) x = -x, putchar('-');
	if (x > 9) write(x / 10);
	putchar(x % 10 + '0');
}
template <typename T> void writeln(T x) {
	write(x);
	puts("");
}
int n; char s[MAXN];
int a[MAXN], b[MAXN];
int main() {
	read(n), scanf("%s", s + 1);
	for (int i = 1; i <= n; i++)
		a[i] = a[i - 1] + (s[i] == '#');
	for (int i = n; i >= 1; i--)
		b[i] = b[i + 1] + (s[i] == '.');
	int ans = n;
	for (int i = 0; i <= n; i++)
		chkmin(ans, a[i] + b[i + 1]);
	writeln(ans);
	return 0;
}

【D】 Three Colors

【思路要点】

  • 补集转化,考虑从所有方案中减去 R ≥ G + B R\geq G+B RG+B G ≥ R + B G\geq R+B GR+B B ≥ G + R B\geq G+R BG+R 的方案。
  • 这样的计算方法会将 B = 0 , R = G B=0,R=G B=0,R=G R = 0 , B = G R=0,B=G R=0,B=G G = 0 , R = B G=0,R=B G=0,R=B 的方案减去两次,再加回来即可。
  • 暴力 d p dp dp ,时间复杂度 O ( N × ∑ a i ) O(N\times\sum a_i) O(N×ai)

【代码】

#include<bits/stdc++.h>
using namespace std;
const int MAXN = 305;
const int P = 998244353;
typedef long long ll;
typedef long double ld;
typedef unsigned long long ull;
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;
}
template <typename T> void write(T x) {
	if (x < 0) x = -x, putchar('-');
	if (x > 9) write(x / 10);
	putchar(x % 10 + '0');
}
template <typename T> void writeln(T x) {
	write(x);
	puts("");
}
int n, sum, dp[MAXN][MAXN * 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;
	else return 1ll * tmp * tmp % P * x % P;
}
int main() {
	read(n), sum = 0;
	dp[0][0][0] = dp[0][0][1] = 1;
	for (int i = 1; i <= n; i++) {
		int x; read(x); sum += x;
		for (int j = 0; j <= sum; j++) {
			dp[i][j][0] = dp[i - 1][j][0];
			dp[i][j][1] = dp[i - 1][j][1] * 2 % P;
			if (j >= x) {
				update(dp[i][j][0], dp[i - 1][j - x][0]);
				update(dp[i][j][1], dp[i - 1][j - x][1]);
			}
		}
	}
	int ans = power(3, n);
	for (int i = (sum + 1) / 2; i <= sum; i++)
		update(ans, P - 3ll * dp[n][i][1] % P);
	if (sum % 2 == 0) {
		//cerr << dp[n][sum / 2][0] << endl;
		update(ans, 3ll * dp[n][sum / 2][0] % P);
	}
	writeln(ans);
	return 0;
}

【E】 Polynomial Divisors

【思路要点】

  • f ( i ) ≡ 0   ( m o d   p ) f(i)\equiv0\ (mod\ p) f(i)0 (mod p) 等价于 f ( i ) f(i) f(i) 在模 p p p 意义下存在因式 ( x − i ) (x-i) (xi)
  • 因此 f ( i ) f(i) f(i) 在模 p p p 意义下始终为零等价于 f ( i ) f(i) f(i) 在模 p p p 意义下存在因式 ∏ i = 0 p − 1 ( x − i ) ≡ x p − x   ( m o d   p ) \prod_{i=0}^{p-1}(x-i)\equiv x^p-x\ (mod\ p) i=0p1(xi)xpx (mod p)
  • 那么,满足条件的 p p p 或是所有 a i a_i ai 的公因数,或在 N N N 以内。
  • 所有 a i a_i ai 的公因数一定可以作为答案,在 N N N 以内的 p p p 可以 O ( N ) O(N) O(N) 判断。
  • 时间复杂度 O ( N L o g V + V + N 2 L o g N ) O(NLogV+\sqrt{V}+\frac{N^2}{LogN}) O(NLogV+V +LogNN2)

【代码】

#include<bits/stdc++.h>
using namespace std;
const int MAXN = 1e4 + 5;
typedef long long ll;
typedef long double ld;
typedef unsigned long long ull;
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;
}
template <typename T> void write(T x) {
	if (x < 0) x = -x, putchar('-');
	if (x > 9) write(x / 10);
	putchar(x % 10 + '0');
}
template <typename T> void writeln(T x) {
	write(x);
	puts("");
}
int tot, prime[MAXN], f[MAXN];
void sieve(int n) {
	for (int i = 2; i <= n; i++) {
		if (f[i] == 0) prime[++tot] = f[i] = 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];
		}
	}
}
set <int> ans;
int n, gcd, a[MAXN];
void factor(int x) {
	for (int i = 2; i * i <= x; i++)
		while (x % i == 0) {
			x /= i;
			ans.insert(i);
		}
	if (x != 1) ans.insert(x);
}
void update(int &x, int y, int p) {
	x = (x + y) % p;
}
void test(int p) {
	static int sum[MAXN];
	for (int i = 0; i <= p - 2; i++)
		sum[i] = 0;
	for (int i = 0; i <= n; i++)
		update(sum[i % (p - 1)], a[i], p);
	for (int i = 0; i <= p - 2; i++)
		if (sum[i] != 0) return;
	ans.insert(p);
}
int main() {
	read(n), sieve(n);
	for (int i = n; i >= 0; i--) {
		read(a[i]);
		if (a[i] != 0) {
			int tmp = a[i];
			if (tmp < 0) tmp = -tmp;
			if (gcd == 0) gcd = tmp;
			else gcd = __gcd(gcd, tmp);
		}
	}
	factor(gcd);
	for (int i = 1; i <= tot; i++)
		if (a[0] % prime[i] == 0) test(prime[i]);
	for (auto x : ans)
		writeln(x);
	return 0;
}

【F】 Banned X

【思路要点】

  • 首先枚举 0 0 0 的个数 i i i ,计算 f ( N − i , X ) f(N-i,X) f(Ni,X) 表示由 N − i N-i Ni 1 , 2 1,2 1,2 构成的,不存在和为 X X X 的子段的序列的个数,将 ( N i ) × f ( N − i , X ) \binom{N}{i}\times f(N-i,X) (iN)×f(Ni,X) 计入答案。
  • 考虑由 1 , 2 1,2 1,2 构成的序列不存在和为 X X X 的子段的条件,需要满足以下其一:
    ( 1 ) (1) (1) 、序列和 S S S 不足 X X X
    ( 2 ) (2) (2) S − X S-X SX 是奇数,且序列仅由 2 2 2 组成。
    ( 3 ) (3) (3) S − X S-X SX 是奇数,且任何一个 1 1 1 及其左侧所有数的和不足 X X X ,任何一个 1 1 1 及其右侧所有数的和不足 X X X
  • 枚举序列和 S S S ,对于条件 ( 1 ) , ( 2 ) (1),(2) (1),(2) ,可以用组合数简单计算。
  • 对于条件 ( 3 ) (3) (3) ,首先 S − X S-X SX 是奇数,其次序列右侧和左侧都应有至少 S − X + 1 2 \frac{S-X+1}{2} 2SX+1 2 2 2 ,中间可任意摆放,用组合数计算方案数即可。
  • 时间复杂度 O ( N 2 ) O(N^2) O(N2)

【代码】

#include<bits/stdc++.h>
using namespace std;
const int MAXN = 3e3 + 5;
const int P = 998244353;
typedef long long ll;
typedef long double ld;
typedef unsigned long long ull;
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;
}
template <typename T> void write(T x) {
	if (x < 0) x = -x, putchar('-');
	if (x > 9) write(x / 10);
	putchar(x % 10 + '0');
}
template <typename T> void writeln(T x) {
	write(x);
	puts("");
}
int binom[MAXN][MAXN];
void update(int &x, int y) {
	x += y;
	if (x >= P) x -= P;
}
int getans(int n, int x) {
	int ans = 0;
	if (2 * n < x || x % 2 == 1) ans = 1;
	for (int i = n; i <= 2 * n - 1; i++) {
		if (i < x) update(ans, binom[n][i - n]);
		else {
			if ((i - x) % 2 == 0) continue;
			int cnt = (i - x + 1) / 2;
			if (2 * cnt >= n) continue;
			int rp = n - 2 * cnt, rs = i - 4 * cnt;
			if (rs >= rp && rs <= 2 * rp) update(ans, binom[rp][rs - rp]);
		}
	}
	return ans;
}
int main() {
	int n, x; read(n), read(x);
	for (int i = 0; i <= n; i++) {
		binom[i][0] = 1;
		for (int j = 1; j <= i; j++)
			binom[i][j] = (binom[i - 1][j - 1] + binom[i - 1][j]) % P;
	}
	int ans = 0;
	for (int i = 0; i <= n; i++)
		update(ans, 1ll * binom[n][i] * getans(n - i, x) % P);
	writeln(ans);
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值