Codeforces Round #687 (Div. 1) 简要题解

原题解

A

题意

一个长度为 (n)(01) 字符串,你可以花费 (x) 的代价把一个 (0) 改成 (1),也可以花费 (y) 的代价把第一个字符移走。

求让 (p,p+k,p+2k,dots) 位置上的字符全为 (1) 的最小代价。

(1le ple nle 10^5)(1le kle n)(1le x,yle 10^4)

多测,(tle 100)(n) 的总和不超过 (10^5)

Solution

模拟题。算出以每个下标为初始 (p) 要改多少个 (0) 之后枚举移走的字符个数即可,(O(sum n))

Code

#include <bits/stdc++.h>

template <class T>
inline void read(T &res)
{
	res = 0; bool bo = 0; char c;
	while (((c = getchar()) < '0' || c > '9') && c != '-');
	if (c == '-') bo = 1; else res = c - 48;
	while ((c = getchar()) >= '0' && c <= '9')
		res = (res << 3) + (res << 1) + (c - 48);
	if (bo) res = ~res + 1;
}

template <class T>
inline T Min(const T &a, const T &b) {return a < b ? a : b;}

const int N = 1e5 + 5, INF = 0x3f3f3f3f;

int n, p, k, x, y, sum[N];
char s[N];

void work()
{
	read(n); read(p); read(k);
	scanf("%s", s + 1); read(x); read(y);
	int ans = INF;
	for (int i = n; i >= 1; i--)
		sum[i] = s[i] == '0', sum[i] += i + k <= n ? sum[i + k] : 0;
	for (int i = 0; i <= n - p; i++)
		ans = Min(ans, y * i + x * sum[i + p]);
	printf("%d
", ans);
}

int main()
{
	int T; read(T);
	while (T--) work();
	return 0;
}

B

题意

一个长度为 (n) 的单调不减序列 (a),定义一次操作为选定 (a) 中两个相邻的数合并,合并之后这个数等于原来两个数的 ( ext{xor})

求让这个序列不再单调不减的最小步数,无解输出 (-1)

(2le nle10^5)(1le a_ile10^9)

Solution

如果有连续的三个数,它们的最高位相同,则把后面两个合并即可,故 (n) 大于两倍 (log a) 则答案为 (1)

否则暴力枚举两个相邻的区间,判断这两个区间异或和大小关系即可。

(O(n+log^3 a))

Code

#include <bits/stdc++.h>

template <class T>
inline void read(T &res)
{
	res = 0; bool bo = 0; char c;
	while (((c = getchar()) < '0' || c > '9') && c != '-');
	if (c == '-') bo = 1; else res = c - 48;
	while ((c = getchar()) >= '0' && c <= '9')
		res = (res << 3) + (res << 1) + (c - 48);
	if (bo) res = ~res + 1;
}

template <class T>
inline T Min(const T &a, const T &b) {return a < b ? a : b;}

const int N = 1e5 + 5;

int n, a[N], ans = 20050131;

int main()
{
	read(n);
	for (int i = 1; i <= n; i++) read(a[i]);
	if (n > 64) return puts("1"), 0;
	for (int i = 1; i <= n; i++) a[i] ^= a[i - 1];
	for (int i = 1; i < n; i++)
		for (int j = 0; j < i; j++)
			for (int k = i + 1; k <= n; k++)
				if ((a[i] ^ a[j]) > (a[k] ^ a[i]))
					ans = Min(ans, k - j);
	return std::cout << (ans == 20050131 ? -1 : ans - 2) << std::endl, 0;
}

C

题意

(n) 个 BOSS,你可以用任意顺序击杀,第 (i) 个 BOSS 有个属性值 (c_i),可正可负。

你有两个参数 (delta)(sum),击杀第 (i) 个 BOSS 之后会令 (sum+=delta,delta+=c_i)

整个过程中你可以随时让你的 (delta) 清零,最多清零 (k) 次,求击杀所有 (n) 个 BOSS 后最大的 (sum)

(1le nle 5 imes10^5)(0le kle 5 imes10^5)(|c_i|le 10^6)

Solution

答案即为分成 (k+1) 个集合,使得所有集合降序排序后的二阶前缀和之和最大。

考虑贪心:一开始有 (k+1) 个空集,(c) 从大到小,每次都接在 (c) 之和最大的集合上面,总复杂度 (O(nlog n))

证明可以归纳:(c) 为正时显然,否则假如已经证明了最后 (i)(c) 加入时贪心策略满足,考虑如果倒数第 (i+1)(c) 没有接在最大的集合上面,则倒数第 (i)(c) 一定会接在最大的集合上面,两者互换可以得到更优答案。

Code

#include <bits/stdc++.h>

template <class T>
inline void read(T &res)
{
	res = 0; bool bo = 0; char c;
	while (((c = getchar()) < '0' || c > '9') && c != '-');
	if (c == '-') bo = 1; else res = c - 48;
	while ((c = getchar()) >= '0' && c <= '9')
		res = (res << 3) + (res << 1) + (c - 48);
	if (bo) res = ~res + 1;
}

typedef long long ll;

const int N = 5e5 + 5;

int n, k, c[N];
ll ans;

std::priority_queue<ll> pq;

int main()
{
	read(n); read(k);
	for (int i = 1; i <= n; i++) read(c[i]);
	std::sort(c + 1, c + n + 1);
	for (int i = 1; i <= k + 1; i++) pq.push(0);
	for (int i = n; i >= 1; i--)
	{
		ll num = pq.top(); pq.pop();
		ans += num; pq.push(num + c[i]);
	}
	return std::cout << ans << std::endl, 0;
}

D

题意

(n) 个物品,第 (i) 个物品在 (t_i) 时刻于数轴上点 (x_i) 出现,保证 (t_i) 严格递增,(x_i) 互不相同。

初始 (0) 时刻你在位置 (0),每个单位时间可以移动一步或者不移动。

当且仅当恰好时刻 (t_i) 恰好在位置 (x_i) 时,你才能拿走第 (i) 个物品,拿走物品用的时间为 (0)

任何时候,你也可以花费 (0) 单位时间在当前位置放一个雕塑,雕塑同样可以在特定时间拿走当前位置上的物品,但是在一个新的位置上放雕塑的时候,原来的雕塑会被销毁。

特别地,如果 (t) 时刻原来雕塑在位置 (x),并把新雕塑放在了位置 (y),则在这个时刻 (x)(y) 上的物品都会被收集

判断你是否能拿走所有物品,(1le nle 5000)(1le t_ile 10^9)(|x_i|le 10^9)

Solution

利用任何时候只能有一个雕塑的条件,可以 DP:

(f_i) 表示能否到达 (x_i) 收集物品;

(g_i) 表示能否到达 (x_{i-1}) 收集物品(物品 (i) 让雕塑收集);

(h_i) 表示在 (x_i) 放上雕塑(必须在时间 (t_{i-1}) 之后)的最早时间。

转移基本上就是分当前点到达下一个点,以及对后面的一个物品所在位置提前放雕塑两种情况讨论,(O(n^2))

Code

#include <bits/stdc++.h>

template <class T>
inline void read(T &res)
{
	res = 0; bool bo = 0; char c;
	while (((c = getchar()) < '0' || c > '9') && c != '-');
	if (c == '-') bo = 1; else res = c - 48;
	while ((c = getchar()) >= '0' && c <= '9')
		res = (res << 3) + (res << 1) + (c - 48);
	if (bo) res = ~res + 1;
}

template <class T>
inline T Abs(T x) {return x < 0 ? -x : x;}

template <class T>
inline T Min(const T &a, const T &b) {return a < b ? a : b;}

template <class T>
inline T Max(const T &a, const T &b) {return a > b ? a : b;}

const int N = 5005;

int n, t[N], x[N], h[N];
bool a[N][N], f[N], g[N];

bool judge(int i, int ti, int j, int k, int m)
{
	if (Abs(x[i] - x[k]) > 1000000000) return 0; 
	int tk = Abs(x[i] - x[k]) + ti; if (tk < m) tk = m;
	return tk <= t[j] && t[j] - tk >= Abs(x[k] - x[j]);
}

int main()
{
	read(n); f[0] = 1;
	for (int i = 0; i <= n; i++) h[i] = 1061109567;
	for (int i = 1; i <= n; i++) read(t[i]), read(x[i]), a[i][i] = 1;
	for (int i = 1; i <= n; i++)
		for (int j = i + 1; j <= n; j++)
			a[i][j] = a[i][j - 1] && t[j] - t[j - 1] >= Abs(x[j] - x[j - 1]);
	for (int i = 0; i < n; i++)
	{
		if (f[i])
		{
			if (t[i + 1] - t[i] >= Abs(x[i + 1] - x[i])) f[i + 1] = 1,
				h[i + 1] = Min(h[i + 1], t[i] + Abs(x[i + 1] - x[i]));
			for (int j = i + 2; j <= n; j++)
				if (a[i + 1][j - 1] && judge(i, t[i], i + 1, j, t[i]))
					g[j] = 1;
		}
		if (g[i])
		{
			if (t[i + 1] - t[i - 1] >= Abs(x[i + 1] - x[i - 1]))
				f[i + 1] = 1, h[i + 1] = Min(h[i + 1], Max(t[i],
					t[i - 1] + Abs(x[i + 1] - x[i - 1])));
			for (int j = i + 2; j <= n; j++)
				if (a[i + 1][j - 1] && judge(i - 1, t[i - 1], i + 1, j, t[i]))
					g[j] = 1;
		}
		if (h[i] < 1061109567)
		{
			if (t[i + 1] - h[i] >= Abs(x[i + 1] - x[i])) f[i + 1] = 1,
				h[i + 1] = Min(h[i + 1], Max(t[i], h[i] + Abs(x[i + 1] - x[i])));
			for (int j = i + 2; j <= n; j++)
				if (a[i + 1][j - 1] && judge(i, h[i], i + 1, j, t[i]))
					g[j] = 1;
		}
	}
	return puts(f[n] || g[n] ? "YES" : "NO"), 0;
}

E

题意

对于一个 (k) 位二进制数 (x),定义 (p(x)) 表示 (sum_{i=0}^{k-1}c_i[x的第i位为1])(x) 的第 (i) 位定义为 (lfloorfrac x{2^i} floormod 2)

(n) 个数 (a_{1dots n}),满足 (a_iin[l_i,r_i]),求 (sum_{i=1}^{n-1}p(a_ioplus a_{i+1})) 的最小值,(oplus) 为异或运算。

(2le nle 50)(1le kle 50)(0le l_ile r_i<2^k)(0le c_ile 10^{12})

Solution

考虑把所有的区间在值域为 ([0,2^k)) 的线段树上拆成 (O(k)) 个区间,不难发现:

定义线段树根的深度为 (0),则线段树上任何一个第 (i) 层的节点,对应的区间都表示最高 (i) 位固定,最低 (k-i) 位任意。

拆完区间之后,问题可以视为对每个数 (a_i) 都选择一个它拆出的子区间计算最优答案。

利用笛卡尔树的思想,可以得到一个区间 DP:(f_{l,r,i,x,y}) 表示就 (ge i) 的位,(a_{ldots r}) 的所有数只能选择长度大于等于 (2^i) 的子区间,(a_{l-1})(a_{r+1}) 分别为 (x)(y)(我们只需关心 (x)(y) 的第 (i) 到第 (k-1) 位)的最优答案。

边界为 (f_{l,l-1,k,*,*}=0)

(1)如果 (a_{ldots r}) 选择的子区间长度都大于 (2^i)

[f_{l,r,i,x,y}leftarrow f_{l,r,i+1,x,y}+c_i[lfloorfrac x{2^i} floormod 2 elfloorfrac y{2^i} floormod 2] ]

(a_{ldots r}) 的第 (i) 位可以随便取,如果 (x)(y) 的第 (i) 位不同则会产生一次代价。

(2)枚举 (lle midle r)(a_{mid}) 选择的子区间长度为 (2^i),区间里的一个数为 (z)

[f_{l,r,i,x,y}leftarrow f_{l,mid-1,i,x,z}+f_{mid+1,r,i,z,y} ]

对于 (l=1)(r=n) 的情况需要一些特殊处理,推荐使用记忆化搜索。

根据线段树的性质,线段树提取区间时在一层内经过的节点数 (le 4),故对于指定的 (l,r,i),本质不同的 (x)(y) 分别都只有 (4) 种。

总复杂度 (O(n^4))

Code

#include <bits/stdc++.h>

template <class T>
inline void read(T &res)
{
	res = 0; bool bo = 0; char c;
	while (((c = getchar()) < '0' || c > '9') && c != '-');
	if (c == '-') bo = 1; else res = c - 48;
	while ((c = getchar()) >= '0' && c <= '9')
		res = (res << 3) + (res << 1) + (c - 48);
	if (bo) res = ~res + 1;
}

typedef long long ll;

const int N = 55;
const ll INF = 1e18;

int n, k, w[N][N];
ll c[N], f[N][N][N][4][4];
std::vector<int> fa[N][N];
std::vector<bool> is[N][N];
std::vector<ll> a[N][N];

void orz(ll l, ll r, ll s, ll e, int i, int j, int f)
{
	if (e < l || s > r) return;
	a[i][j].push_back(l); fa[i][j].push_back(f); w[i][j]++;
	if (s <= l && r <= e) return (void) is[i][j].push_back(1);
	else is[i][j].push_back(0);
	ll mid = l + r >> 1; int tmp = w[i][j] - 1;
	orz(l, mid, s, e, i, j - 1, tmp);
	orz(mid + 1, r, s, e, i, j - 1, tmp);
}

ll dp(int l, int r, int num, int x, int y)
{
	if (num == k && l > r) return 0;
	if (f[l][r][num][x][y] != -1) return f[l][r][num][x][y];
	ll res = num < k ? dp(l, r, num + 1,
		fa[l - 1][num][x], fa[r + 1][num][y]) : INF;
	if (res < INF && l > 1 && r < n && ((a[l - 1][num][x] >> num) & 1)
		!= ((a[r + 1][num][y] >> num) & 1)) res += c[num];
	for (int i = l; i <= r; i++) for (int j = 0; j < w[i][num]; j++)
	{
		if (!is[i][num][j]) continue;
		ll lc = dp(l, i - 1, num, x, j), rc = dp(i + 1, r, num, j, y);
		if (lc + rc < res) res = lc + rc;
	}
	return f[l][r][num][x][y] = res;
}

int main()
{
	ll l, r;
	read(n); read(k);
	for (int i = 0; i <= k; i++) fa[0][i].push_back(0),
		fa[n + 1][i].push_back(0);
	for (int i = 1; i <= n; i++) read(l), read(r),
		orz(0, (1ll << k) - 1, l, r, i, k, 0);
	for (int i = 0; i < k; i++) read(c[i]);
	memset(f, -1, sizeof(f));
	return std::cout << dp(1, n, 0, 0, 0) << std::endl, 0;
}
原文地址:https://www.cnblogs.com/xyz32768/p/14076686.html