Educational Codeforces Round 56 (Rated for Div. 2)

唠嗑

这一场题目基本比较模板,所以就稍微写一下题解吧

题解

A. Dice Rolling

有一个色子,六个面为 2 2 2 7 7 7,每一次给定一个数 x x x,问可以掷多少次色子使得最上面的数的和为 x x x

解法
  • 可以发现,尽量用 7 7 7就可以了,答案显然为 ⌈ x 7 ⌉ \lceil\frac{x}{7}\rceil 7x
代码
#include <bits/stdc++.h>
using namespace std;
template <typename T> void chkmax(T &x, T y) {x = x > y ? x : y;}
template <typename T> void chkmin(T &x, T y) {x = x < y ? x : y;}
template <typename T> void read(T &x) {
	x = 0; int f = 1; char c = getchar();
	while (!isdigit(c)) {if (c == '-') f = -1; c = getchar();}
	while (isdigit(c)) x = x * 10 + c - '0', c = getchar(); x *= f;
}
int main() {
	int T; read(T);
	while (T--) {
		int x; read(x);
		int y = x % 7, z = x / 7;
		if (y == 0) {cout << z << "\n"; continue;}
		cout << z + 1 << "\n";
	}
	return 0;
}
B. Letters Rearranging

每一次给定一个字符串 s s s,问能否构造一个顺序使得最后的字符串不是回文串。

解法
  • 直接sort即可
代码
#include <bits/stdc++.h>
using namespace std;
template <typename T> void chkmax(T &x, T y) {x = x > y ? x : y;}
template <typename T> void chkmin(T &x, T y) {x = x < y ? x : y;}
template <typename T> void read(T &x) {
	x = 0; int f = 1; char c = getchar();
	while (!isdigit(c)) {if (c == '-') f = -1; c = getchar();}
	while (isdigit(c)) x = x * 10 + c - '0', c = getchar(); x *= f;
}
int s[100];
int main() {
	int T; cin >> T;
	while (T--) {
		string st; cin >> st;
		memset(s, 0, sizeof(s));
		int l = st.size(), mx = 0;
		for (int i = 0; i < l; i++) s[st[i] - 'a']++, chkmax(mx, s[st[i] - 'a']);
		if (mx == l) {cout << "-1\n"; continue;}
		for (int i = 0; i < 26; i++)
			for (int j = 1; j <= s[i]; j++)
				putchar(i + 'a');
		cout << endl;
	}
	return 0;
}
C. Mishka and the Last Exam

有一个长度为 n n n的序列 a a a,告诉你长度为 n 2 \frac{n}{2} 2n的序列 b b b满足 b [ i ] = a [ i ] + a [ n − i + 1 ] b[i]=a[i]+a[n-i+1] b[i]=a[i]+a[ni+1]。问是否存在单调不降的序列 a a a,如果有输出方案。
n ≤ 2 × 1 0 5 n≤2×10^5 n2×105

解法
  • 显然,我们肯定让 a [ 1 ] = 0 , a [ n ] = b [ n / 2 ] a[1]=0,a[n]=b[n/2] a[1]=0,a[n]=b[n/2]
  • 然后为了使尽量单调,所以我们让 a [ i ] a[i] a[i]尽量小就可以了。
  • 时间复杂度: O ( n ) O(n) O(n)
代码
#include <bits/stdc++.h>
#define int long long
#define N 300010
using namespace std;
template <typename T> void chkmax(T &x, T y) {x = x > y ? x : y;}
template <typename T> void chkmin(T &x, T y) {x = x < y ? x : y;}
template <typename T> void read(T &x) {
	x = 0; int f = 1; char c = getchar();
	while (!isdigit(c)) {if (c == '-') f = -1; c = getchar();}
	while (isdigit(c)) x = x * 10 + c - '0', c = getchar(); x *= f;
}
int a[N], b[N];
signed main() {
	int n; read(n);
	for (int i = 1; i <= n / 2; i++) read(b[i]);
	a[1] = 0, a[n] = b[1];
	for (int i = 2; i <= n / 2; i++) {
		int x = LONG_LONG_MAX;
		if (b[i] - a[i - 1] <= a[n - i + 2]) x = a[i - 1];
		if (b[i] >= a[n - i + 2] && b[i] - a[n - i + 2] >= a[i - 1]) chkmin(x, b[i] - a[n - i + 2]);
		a[i] = x, a[n - i + 1] = b[i] - a[i];
	}
	for (int i = 1; i <= n; i++) cout << a[i] << ' '; cout << "\n";
	return 0;
}
D. Beautiful Graph

给定一个 n n n个点, m m m条边的无向图,每一个点的点权可以为 1 , 2 , 3 1,2,3 1,2,3,相邻的点权之和不能为偶数,问有多少种点权赋值方案。

解法
  • 对于每一个联通块单独考虑,答案显然为所有联通块答案之积。
  • 显然可以发现,相邻两个点的点权奇偶性不同,那么就可以变成一个二分图。假设白点有 x x x个,黑点有 y y y个,那么答案就为 2 x + 2 y 2^x+2^y 2x+2y
  • 时间复杂度: O ( m ) O(m) O(m)
代码
#include <bits/stdc++.h>
#define Mod 998244353
#define N 300010
using namespace std;
template <typename T> void chkmax(T &x, T y) {x = x > y ? x : y;}
template <typename T> void chkmin(T &x, T y) {x = x < y ? x : y;}
template <typename T> void read(T &x) {
	x = 0; int f = 1; char c = getchar();
	while (!isdigit(c)) {if (c == '-') f = -1; c = getchar();}
	while (isdigit(c)) x = x * 10 + c - '0', c = getchar(); x *= f;
}
struct Edge {int next, num;} e[N * 10];
int s, cnt, tot, flag, vis[N], col[N];
void add(int x, int y) {
	e[++cnt] = (Edge) {e[x].next, y};
	e[x].next = cnt;
}
void dfs(int x, int c) {
	if (vis[x]) {if (col[x] != c) flag = false; return;}
	vis[x] = 1, col[x] = c, tot++; if (c) s++;
	for (int p = e[x].next; p; p = e[p].next) dfs(e[p].num, c ^ 1);
}
int Pow(int x, int y) {
	int ret = 1;
	while (y) {
		if (y & 1) ret = 1ll * ret * x % Mod;
		y >>= 1, x = 1ll * x * x % Mod;
	}
	return ret;
}
int main() {
	int T; read(T);
	while (T--) {
		int n, m; read(n), read(m); cnt = n;
		for (int i = 1; i <= n; i++) e[i].next = 0, vis[i] = 0, col[i] = 0;
		for (int i = 1; i <= m; i++) {
			int x, y; read(x), read(y);
			add(x, y), add(y, x);
		}
		int ans = 1;
		for (int i = 1; i <= n; i++)
			if (!vis[i]) {
				flag = true, s = tot = 0; dfs(i, 0);
				if (!flag) {ans = 0; continue;}
				ans = 1ll * ans * ((Pow(2, s) + Pow(2, tot - s)) % Mod) % Mod;
			}
		cout << ans << "\n";
	}
	return 0;
}
E. Intersection of Permutations

给定 1 − n 1-n 1n的排列 a a a b b b,有 q q q组操作:1.交换 b [ x ] , b [ y ] b[x],b[y] b[x],b[y] 2.询问 a [ l ] , … , a [ r ] a[l],\dots,a[r] a[l],,a[r]的数集与 b [ L ] , … , b [ R ] b[L],\dots,b[R] b[L],,b[R]的数集的交集的大小。
n , q ≤ 2 × 1 0 5 n,q\leq 2\times 10^5 n,q2×105

解法
  • 考虑将 b b b中的元素变成在 a a a中出现的位置,那么就变成每一次询问 b b b中的一段区间 [ L , R ] [L,R] [L,R]中的元素大小在 [ l , r ] [l,r] [l,r]的有多少个。
  • 显然可以使用树套树实现,但是可能会出现空间不够的情况。
  • 那么不妨考虑cdq分治,不过要将操作拆成多个。
  • 时间复杂度: O ( q log ⁡ 2 n ) O(q\log^2 n) O(qlog2n)
代码
#include <bits/stdc++.h>
#define N 200010
using namespace std;
template <typename T> void chkmax(T &x, T y) {x = x > y ? x : y;}
template <typename T> void chkmin(T &x, T y) {x = x < y ? x : y;}
template <typename T> void read(T &x) {
	x = 0; int f = 1; char c = getchar();
	while (!isdigit(c)) {if (c == '-') f = -1; c = getchar();}
	while (isdigit(c)) x = x * 10 + c - '0', c = getchar(); x *= f;
}
struct Node {
	int x, l, r, t, id;
	Node() {}
	Node(int _x, int _l, int _r, int _t, int _id) {x = _x; l = _l; r = _r; t = _t; id = _id;}
} q[N * 10], cur[N * 10]; 
int n, c[N], a[N], b[N], posa[N], posb[N], ans[N];
void add(int x, int v) {for (; x <= n; x += x & -x) c[x] += v;}
int query(int x) {int ans = 0; for(; x; x -= x & -x) ans += c[x]; return ans;}
void cdq(int l, int r) {
	if (l >= r) return;
	int mid = l + r >> 1;
	cdq(l, mid), cdq(mid + 1, r);
	int v = l, j = l, k = mid + 1;
	while (j <= mid && k <= r) {
		if (q[j].x <= q[k].x) {
			if (q[j].id == 0) add(q[j].l, q[j].t);
			cur[v++] = q[j++];
		} else {
			if (q[k].id != 0) ans[q[k].id] += q[k].t * (query(q[k].r) - query(q[k].l - 1));
			cur[v++] = q[k++];
		}
	}
	while (j <= mid) {
		if (q[j].id == 0) add(q[j].l, q[j].t);
		cur[v++] = q[j++];
	}
	while (k <= r) {
		if(q[k].id != 0) ans[q[k].id] += q[k].t * (query(q[k].r) - query(q[k].l - 1));
		cur[v++] = q[k++];
	}
	for (int i = l; i <= mid; i++) if(q[i].id == 0) add(q[i].l, -q[i].t);
	for (int i = l; i <= r; i++) q[i] = cur[i]; 
}
int main() {
	int m; read(n), read(m);
	for (int i = 1; i <= n; i++) read(a[i]), posa[a[i]] = i;
	for (int i = 1; i <= n; i++) read(b[i]), posb[b[i]] = i;
	int tot = 0, cnt = 0;;
	for (int i = 1; i <= n; i++) q[++tot] = Node(posb[a[i]], i, 0, 1, 0);
	for (int i = 1; i <= m; i++) {
		int op, A, B, C, D; read(op), read(A), read(B);
		if (op == 1) {
			read(C), read(D);
			q[++tot] = Node(C - 1, A, B, -1, ++cnt);
			q[++tot] = Node(D, A, B, 1, cnt);
		} else {
			q[++tot] = Node(A, posa[b[A]], 0, -1, 0);
			q[++tot] = Node(B, posa[b[B]], 0, -1, 0);
			swap(b[A], b[B]);
			q[++tot] = Node(A, posa[b[A]], 0, 1, 0);
			q[++tot] = Node(B, posa[b[B]], 0, 1, 0);
		}
	}
	cdq(1, tot);
	for (int i = 1; i <= cnt; i++) cout << ans[i] << "\n";
	return 0;
}
F. Vasya and Array

给定一个长度为 n n n的序列,有些位置没有填入数字。有数字的位置数字大小在 [ 1 , k ] [1,k] [1,k]。现在要将没有数字的位置填入 [ 1 , k ] [1,k] [1,k]中的任意一个,满足不存在一个连续相同颜色的区间长度不小于 l e n len len。问有多少种方案。
l e n ≤ n ≤ 1 0 5 , k ≤ 100 len\leq n\leq 10^5,k\leq 100 lenn105,k100

解法
  • 考虑这样一个dp:设 f [ i ] [ j ] f[i][j] f[i][j]表示前 i − 1 i-1 i1位已经填好并且合法,且第 i i i位的元素为 j j j的方案数。
  • 同时,我们定义 s [ i ] = ∑ j = 1 k f [ i ] [ j ] s[i]=\sum_{j=1}^k f[i][j] s[i]=j=1kf[i][j]
  • 那么,如果我们不考虑合不合法的情况,那么 f [ i ] [ j ] = s [ i − 1 ]    ( a [ i ] = − 1 ∣ ∣ a [ i ] = j ) f[i][j]=s[i-1]\ \ (a[i]=-1||a[i]=j) f[i][j]=s[i1]  (a[i]=1a[i]=j)
  • 然后考虑如何去除不合法的情况,如果 [ i − l e n + 1 , i ] [i-len+1,i] [ilen+1,i]这段区间都可以变成 j j j这个元素,那么不合法的序列的数量就是 s [ i − l e n ] − f [ i − l e n ] [ j ] s[i-len]-f[i-len][j] s[ilen]f[ilen][j]
  • 时间复杂度: O ( n k ) O(nk) O(nk)
代码
#include <bits/stdc++.h>
#define Mod 998244353
#define N 100010
using namespace std;
template <typename T> void chkmin(T &x, T y) {x = x < y ? x : y;}
template <typename T> void chkmax(T &x, T y) {x = x > y ? x : y;}
template <typename T> void read(T &x) {
	x = 0; int f = 1; char c = getchar();
	while (!isdigit(c)) {if (c == '-') f = -1; c = getchar();}
	while (isdigit(c)) x = x * 10 + c - '0', c = getchar(); x *= f;
}
int a[N], s[N], f[N][105], b[N][105];
void add(int &x, int y) {x += y; if (x >= Mod) x -= Mod;}
int main() {
	int n, k, len; read(n), read(k), read(len);
	if (len == 1) return cout << "0\n", 0; s[0] = 1;
	for (int i = 1; i <= n; i++) read(a[i]);
	for (int i = 1; i <= n; i++)
		for (int j = 1; j <= k; j++)
			if (a[i] == j || a[i] == -1) b[i][j] = 1;
	for (int j = 1; j <= k; j++)
		for (int i = 1; i <= n; i++)
			b[i][j] += b[i - 1][j];
	for (int i = 1; i <= n; i++) {
		for (int j = 1; j <= k; j++) {
			if (~a[i] && a[i] != j) continue;
			f[i][j] = s[i - 1];
			if (i >= len && b[i][j] - b[i - len][j] == len)
				add(f[i][j], (f[i - len][j] - s[i - len] + Mod) % Mod);
			add(s[i], f[i][j]);
		}
	}
	cout << s[n] << "\n";
	return 0;
}
G. Multidimensional Queries

k k k维空间给定 n n n个点,然后有 q q q组操作:1.修改某一个点的坐标 2.询问编号在区间 [ l , r ] [l,r] [l,r]的点曼哈顿距离最大是多少。
n , q ≤ 2 × 1 0 5 , k ≤ 5 n,q≤2×10^5,k≤5 n,q2×105,k5

解法
  • 假设现在两个点编号为 i , j i,j i,j,那么这两个点的曼哈顿距离即为 ∑ p = 1 k ∣ x i , p − x j , p ∣ \sum_{p=1}^k |x_{i,p}-x_{j,p}| p=1kxi,pxj,p
  • 然后我们可以考虑怎么除去绝对值,令 c i = 1 / − 1 c_i=1/-1 ci=1/1表示绝对值中的正负性,那么式子就可以变成 ∑ p = 1 k c p x i , p − ∑ p = 1 k c p x j , p \sum_{p=1}^k c_px_{i,p}-\sum_{p=1}^k c_px_{j,p} p=1kcpxi,pp=1kcpxj,p
  • 可以发现,我们只要对减号两边分别维护最大值和最小值即可。对于 2 k 2^k 2k种状态的每一种都要维护。
  • 但是可能会存在一种情况:尽管在这种状态下取到最大值,但是与实际并不符合。
  • 那么我们考虑一下为什么这个是正确的。因为存在不符合的项,那么即为拆完了之后那一项是原来的相反数,一定不会比正确的更优。如果它就是最优解,显然在正确的系数下答案会更大,与之前的假设产生矛盾。所以正确性是有保证的。
  • 时间复杂度: O ( ( n + q ) 2 k log ⁡ n ) O((n+q)2^k\log n) O((n+q)2klogn)
代码
#include <bits/stdc++.h>
#define mp make_pair
#define N 800010
using namespace std;
template <typename T> void chkmax(T &x, T y) {x = x > y ? x : y;}
template <typename T> void chkmin(T &x, T y) {x = x < y ? x : y;}
template <typename T> void read(T &x) {
	x = 0; int f = 1; char c = getchar();
	while (!isdigit(c)) {if (c == '-') f = -1; c = getchar();}
	while (isdigit(c)) x = x * 10 + c - '0', c = getchar(); x *= f;
}
typedef pair <int, int> PI;
int K, a[N][5];
struct SegmentTree {
	int mx[N][32], mn[N][32];
	#define lc k << 1
	#define rc k << 1 | 1
	void update(int k) {
		for (int i = 0; i < (1 << K); i++)
			mx[k][i] = max(mx[lc][i], mx[rc][i]),
			mn[k][i] = min(mn[lc][i], mn[rc][i]);
	}
	void build(int k, int l, int r) {
		if (l == r) {
			for (int i = 0; i < (1 << K); i++)
				for (int j = 0; j < K; j++)
					if ((i >> j) & 1) mx[k][i] += a[l][j], mn[k][i] += a[l][j];
						else mx[k][i] -= a[l][j], mn[k][i] -= a[l][j];
			return;
		}
		int mid = (l + r) >> 1;
		build(lc, l, mid), build(rc, mid + 1, r);
		update(k);
	}
	void modify(int k, int l, int r, int x, int a[5]) {
		if (l == r) {
			for (int i = 0; i < (1 << K); i++) {
				mx[k][i] = mn[k][i] = 0;
				for (int j = 0; j < K; j++)
					if ((i >> j) & 1) mx[k][i] += a[j], mn[k][i] += a[j];
						else mx[k][i] -= a[j], mn[k][i] -= a[j];
			}
			return;
		}
		int mid = (l + r) >> 1;
		if (x <= mid) modify(lc, l, mid, x, a); else modify(rc, mid + 1, r, x, a);
		update(k);
	}
	PI query(int k, int l, int r, int L, int R, int num) {
		if (L <= l && r <= R) return mp(mx[k][num], mn[k][num]);
		int mid = (l + r) >> 1; PI ret = mp(-INT_MAX, INT_MAX);
		if (L <= mid) {
			PI t = query(lc, l, mid, L, R, num);
			chkmax(ret.first, t.first), chkmin(ret.second, t.second);
		}
		if (R > mid) {
			PI t = query(rc, mid + 1, r, L, R, num);
			chkmax(ret.first, t.first), chkmin(ret.second, t.second);
		}
		return ret;
	}
} T;
int main() {
	int n; read(n), read(K);
	for (int i = 1; i <= n; i++)
		for (int j = 0; j < K; j++)
			read(a[i][j]);
	T.build(1, 1, n);
	int m; read(m);
	while (m--) {
		int op; read(op);
		if (op == 2) {
			int l, r, ret = 0; read(l), read(r);
			for (int i = 0; i < (1 << K); i++) {
				pair <int, int> t = T.query(1, 1, n, l, r, i);
				chkmax(ret, t.first - t.second);
			}
			cout << ret << "\n";
		} else {
			int pos, b[5]; read(pos);
			for (int i = 0; i < K; i++) read(b[i]);
			T.modify(1, 1, n, pos, b);
		}
	}
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值