• 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;
    }
    
  • 相关阅读:
    PyQt5库结构
    PyQt5
    QObject
    os.listdir()
    os.getcwd()
    os.chdir()
    os.access()
    __name__
    php 学习笔记之关于时区的那点事
    php 学习笔记之搭建开发环境(mac版)
  • 原文地址:https://www.cnblogs.com/xyz32768/p/14076686.html
Copyright © 2020-2023  润新知