Codeforces Round #778 (Div. 1 + Div. 2, based on Technocup 2022 Final Round)

Codeforces Round #778 (Div. 1 + Div. 2, based on Technocup 2022 Final Round)

拥有良好的比赛体验(

A,B 比较 naive。

C - Alice and the Cake

初始有一块蛋糕,每次可以选择一块蛋糕,设其大小为 \(w\),要求 \(w\geq 2\)

并将其劈为 \(\lfloor \frac{w}{2}\rfloor\)\(\lceil \frac{w}{2}\rceil\) 两块。

现给出最终蛋糕序列,求这是否是能被产生的蛋糕序列。

赛时想了一会自底向上的合并,然而并不知道合并优先级是啥。

然后发现蛋糕大小之和是不会变的,相当于起点也知道,可以直接模拟这个过程。

然后就上了一个队列,每次能消就直接消,交了一发 MLE。

发现当前分到的大小 \(<\) 初始序列的最小值显然是不和法的,可以直接 break,又交了一发 MLE。

然后恍然大悟,改成小根堆。

#include<bits/stdc++.h>
typedef long long LL;
#define rep(i, s, t) for(int i = (s); i <= (t); i ++)
#define per(i, s, t) for(int i = (s); i >= (t); i --)
#define Ede(i, u) for(int i = head[u]; i; i = e[i].nxt)
using namespace std;
 
int read() {
	int x = 0, f = 1; char c = getchar();
	while(c < '0' || c > '9') f = (c == '-') ? -1 : 1, c = getchar();
	while(c >= '0' && c <= '9') x = x * 10 + c - 48, c = getchar();
	return x * f;
}
 
map<int, int> c;
 
int main() {
	int T = read();
	while(T --) {
		int n = read(); LL s = 0;
		c.clear();
		int Mn = 2e9;
		while(n --) {int x = read(); c[x] ++, s += x, Mn = min(Mn, x);}
		priority_queue<LL, vector<LL>, greater<LL> > q;
		q.push(s);
		while(! q.empty()) {
			LL x = q.top(); q.pop();
			if(c.find(x) != c.end()) {if(! -- c[x]) c.erase(x);}
			else {
				if((x >> 1) < (c.begin() -> first)) break;
				q.push(x >> 1);
				q.push((x >> 1) + (x & 1));
			}
		}
		if(c.begin() != c.end()) puts("NO"); else puts("YES");
	}
	return 0;
}

D - Potion Brewing Class

构造长度为 \(n\) 的正整数序列。给定 \(n-1\) 对比例关系,形如 \((i,j,x,y)\) 表示 \(a_i/a_j=x/y\)

保证关系能唯一确定所有数的比值关系(树连通),求序列和的最小值,对 \(998244353\) 取模。

确定根,每个点与根的关系都可以得到,和最小根显然取所有分式的 \(\text{lcm}\),相当于求带 \(\bmod\)\(\text{lcm}\)

考虑每个质因子拆开,直接算每个质因子在分母中出现过的最大次数。

实际上维护一个全局桶就好了,进入子树时加上,离开子树时减去,时刻取 \(\max\)

然后赛时头脑一热写了个可持久化线段树,调了半天,发现如果 \(x=y=1\) 需要令 \(\text{rt}(i)=\text{rt}(fa_i)\)

反正过了就还好(

实际上极限数据空间可能可以卡爆(

#include<bits/stdc++.h>
typedef long long LL;
#define rep(i, s, t) for(int i = (s); i <= (t); i ++)
#define per(i, s, t) for(int i = (s); i >= (t); i --)
#define Ede(i, u) for(int i = head[u]; i; i = e[i].nxt)
using namespace std;
 
const int N = 2e5 + 10;
const int P = 998244353;
 
int read() {
	int x = 0, f = 1; char c = getchar();
	while(c < '0' || c > '9') f = (c == '-') ? -1 : 1, c = getchar();
	while(c >= '0' && c <= '9') x = x * 10 + c - 48, c = getchar();
	return x * f;
}
 
int Pow(int a, int b) {
	int s = 1;
	for(; b; b >>= 1, a = 1LL * a * a % P)
		if(b & 1) s = 1LL * s * a % P;
	return s;
}
 
int n, t, p[N], c[N], id[N];
bool v[N];
 
int cnt, head[N];
struct Edge {int nxt, v, x, y;} e[N << 1];
 
void add(int u, int v, int x, int y) {e[++ cnt] = (Edge) {head[u], v, x, y}; head[u] = cnt;}
 
int rt[N], tot;
struct Tree {int l, r, dat, laz;} tr[N * 40]; // 这里应该要开 log^2 的,但是开不下(
 
int New() {
	tr[++ tot] = (Tree) {0, 0, 0, 0};
	return tot;
}
 
int Bui(int l, int r) {
	int p = New(); if(l == r) return p;
	int mid = (l + r) >> 1;
	tr[p].l = Bui(l, mid);
	tr[p].r = Bui(mid + 1, r);
	return p;
}
 
int Ins(int las, int p, int l, int r, int k, int v, bool o, int h) {
	if(! p || tr[p].laz != h) p = New(), tr[p] = tr[las], tr[p].laz = h;
	if(l == r) {
		tr[p].dat += v;
		if(o) c[k] = max(c[k], tr[p].dat);
		return p;
	}
	int mid = (l + r) >> 1;
	if(k <= mid)
		tr[p].l = Ins(tr[las].l, tr[p].l, l, mid, k, v, o, h);
	else
		tr[p].r = Ins(tr[las].r, tr[p].r, mid + 1, r, k, v, o, h);
	return p; 
}
 
vector<int> arc;
 
void dfs1(int u, int fa, int x, int y) {
	// fa * y / x = u
	for(int i = 1; i <= t && 1LL * p[i] * p[i] <= x; i ++) if(x % p[i] == 0) {
		int num = 0;
		while(x % p[i] == 0) num ++, x /= p[i];
		rt[u] = Ins(rt[fa], rt[u], 1, t, i, num, 0, u), arc.push_back(i);
	}
	if(x > 1)
		rt[u] = Ins(rt[fa], rt[u], 1, t, id[x], 1, 0, u), arc.push_back(id[x]);
 
	for(int i = 1; i <= t && 1LL * p[i] * p[i] <= y; i ++) if(y % p[i] == 0) {
		int num = 0;
		while(y % p[i] == 0) num ++, y /= p[i];
		rt[u] = Ins(rt[fa], rt[u], 1, t, i, - num, 0, u);
	}
	if(y > 1)
		rt[u] = Ins(rt[fa], rt[u], 1, t, id[y], - 1, 0, u);
 
	if(! rt[u]) rt[u] = rt[fa];
	for(int v : arc) Ins(rt[fa], rt[u], 1, t, v, 0, 1, u);
	arc.clear();
 
	Ede(i, u) if(e[i].v != fa) dfs1(e[i].v, u, e[i].x, e[i].y);
}
 
int ans;
 
void dfs2(int u, int num, int fa) {
	ans = (ans + num) % P;
	Ede(i, u) if(e[i].v != fa) {
		int nxt = 1LL * num * e[i].y % P * Pow(e[i].x, P - 2) % P;
		dfs2(e[i].v, nxt, u);
	}
}
 
void clear() {
	ans = tot = cnt = t = 0;
	rep(i, 1, n) c[i] = rt[i] = head[i] = 0, v[i] = false;
}
 
void Work() {
	n = read();
	clear();
	rep(i, 2, n) {
		if(! v[i]) id[p[++ t] = i] = t;
		for(int j = 1; j <= t && p[j] * i <= n; j ++) {
			v[p[j] * i] = true;
			if(i % p[j] == 0) break; 
		}
	}
	rep(i, 2, n) {
		int u = read(), v = read();
		int x = read(), y = read();
		add(u, v, x, y);
		add(v, u, y, x);
	}
	rt[0] = Bui(1, t);
	dfs1(1, 0, 0, 0);
	int one = 1;
	rep(i, 1, t) one = 1LL * one * Pow(p[i], c[i]) % P;
	dfs2(1, one, 0);
	printf("%d\n", ans);
}
 
int main() {int T = read(); while(T --) Work(); return 0;}

E - Arithmetic Operations

给定序列,值域和长度都 \(\leq 10^5\),可以对单点进行任意次修改。

求将序列变为等差数列的最小修改次数。空间 1GB。

没想到竟是根号分治题。

对公差 \(d\) 进行讨论:

  • \(d\leq \sqrt V\),直接枚举每个 \(d\),开一个桶统计 \(a_i-i\times d\) 相同的最大值即可。
  • \(d>\sqrt V\),枚举不改变的点,以它为中心,只有 \(\pm \sqrt V\) 个位置可能也保持不变,统计即可。

空间很大,直接开 \(7\times 10^7\) 的数组当桶,unordered_map 反而会被卡常。

#include<bits/stdc++.h>
typedef long long LL;
#define rep(i, s, t) for(int i = (s); i <= (t); i ++)
#define per(i, s, t) for(int i = (s); i >= (t); i --)
#define Ede(i, u) for(int i = head[u]; i; i = e[i].nxt)
using namespace std;
 
const int N = 1e5 + 10, M = 310;
const int R = 7e7, D = R / 2;
int n, a[N], cnt[R];
 
int read() {
	int x = 0, f = 1; char c = getchar();
	while(c < '0' || c > '9') f = (c == '-') ? -1 : 1, c = getchar();
	while(c >= '0' && c <= '9') x = x * 10 + c - 48, c = getchar();
	return x * f;
}
 
int main() {
	n = read();
	rep(i, 1, n) a[i] = read();
	int ans = 0;
	rep(d, - M, M) {
		rep(i, 1, n) if(a[i] - i * d + D >= 0 && a[i] - i * d + D < R)
				ans = max(ans, ++ cnt[a[i] - i * d + D]);
		rep(i, 1, n) if(a[i] - i * d + D >= 0 && a[i] - i * d + D < R)
				cnt[a[i] - i * d + D] --;
	}
	rep(i, 1, n) {
		rep(j, max(1, i - M), min(i + M, n))
			if(i != j && (a[i] - a[j]) % (i - j) == 0)
				ans = max(ans, (++ cnt[(a[i] - a[j]) / (i - j) + D]) + 1);
		rep(j, max(1, i - M), min(i + M, n))
			if(i != j && (a[i] - a[j]) % (i - j) == 0)
				cnt[(a[i] - a[j]) / (i - j) + D] --;
	}
	printf("%d\n", n - ans);
	return 0; 
}

F - Minimal String Xoration

给定字符串 \(s\),长度为 \(2^n\)。定义 \(T_i(i\in [0,2^n-1])\),其中 \(\forall j\in[0,2^n-1],t_{j}=s_{j\oplus i}\)

求字典序最小的 \(T\)

很有趣的题目。

实际上,可以考虑对所有 \(T_i\) 排序,过程和后缀数组的构造过程十分类似。

\(rk(i,j)\) 表示 \(T_i\),按照前 \(2^j\) 的字典序排序的排名。

显然 \(rk(i,j)\) 可以由 \(rk(i,j-1),rk(i\oplus2^{j-1},j-1)\) 两者的排名得到,优先级按顺序。

因为 \(i\) 的前 \(j\) 位恰由 \(i\) 的前 \(j-1\) 位与 \(i\oplus 2^{j-1}\) 的前 \(j-1\) 位构成。

直接基数排序 + 倍增就可以做到 \(O(n2^n)\) 了。

#include<bits/stdc++.h>
typedef long long LL;
#define rep(i, s, t) for(int i = (s); i <= (t); i ++)
#define per(i, s, t) for(int i = (s); i >= (t); i --)
#define Ede(i, u) for(int i = head[u]; i; i = e[i].nxt)
using namespace std;

const int N = (1 << 18) + 10, M = 20;
int n, m, sa[N], rk[N], SA[N], RK[N], bac[N];
char str[N];

int main() {
	scanf("%d %s", &m, str);
	n = (1 << m) - 1;
	rep(i, 0, n) bac[str[i] - 'a'] ++;
	rep(i, 1, 25) bac[i] += bac[i - 1];
	rep(i, 0, n) sa[-- bac[str[i] - 'a']] = i;
	rk[sa[0]] = 1;
	rep(i, 1, n) rk[sa[i]] = rk[sa[i - 1]] + (str[sa[i]] != str[sa[i - 1]]);
	
	rep(o, 1, m) {
		rep(i, 1, n + 1) bac[i] = 0;
		rep(i, 0, n) ++ bac[rk[i]];
		rep(i, 1, n + 1) bac[i] += bac[i - 1];
		per(i, n, 0) SA[-- bac[rk[sa[i] ^ (1 << (o - 1))]]] = (sa[i] ^ (1 << (o - 1)));
		#define comp(x, y) (rk[x] != rk[y] || rk[x ^ (1 << (o - 1))] != rk[y ^ (1 << (o - 1))])
		RK[SA[0]] = 1;
		rep(i, 1, n) RK[SA[i]] = RK[SA[i - 1]] + comp(SA[i], SA[i - 1]);
		rep(i, 0, n) sa[i] = SA[i], rk[i] = RK[i];
		if(rk[sa[n]] == n) break;
	}
	int ans = sa[0];
	rep(i, 0, n) putchar(str[i ^ ans]); puts("");
	return 0;
}
posted @ 2022-03-22 17:05  LPF'sBlog  阅读(124)  评论(0编辑  收藏  举报