Educational Codeforces Round 125

Educational Codeforces Round 125

A,B,C 比较没意思

D - For Gamers. By Gamers.

\(n\) 种军队可以雇佣,初始有 \(C\) 元钱。

\(m\) 只怪物需要击败,每次都有 \(C\) 元,但每次只能雇佣一种军队。

军队有三个参数:\(c_i,d_i,h_i\),分别表示对于一个单位这种士兵,他的雇佣价钱、攻击力、血量。

怪物有两个参数:\(D_j,H_j\),分别表示该怪物的攻击力、血量。

怪物会一直共计一个单位士兵,要求不能有士兵死亡,求最少要花费的钱数,如果 \(>C\) 输出无解。

注意攻击伤害是连续的,不是离散的。

实际上,设 \(k_{i,j}\) 表示第 \(i\) 种士兵杀死第 \(j\) 只怪物的最小雇佣人数,显然就是满足:

\[\displaystyle \frac{H_j}{k_{i,j}d_i}<\frac{h_i}{D_j} \]

移项得到:

\[\displaystyle k_{i,j}=\lfloor\frac{H_jD_j}{h_id_i}\rfloor+1 \]

那么,用第 \(i\) 种士兵杀死第 \(j\) 只怪物的代价就是 \(c_ik_{i,j}\)

发现这是一个分段函数,下取整很不好维护,于是就有点卡住了。

中间奇思妙想了一段用李超树维护分段函数单点最值,但是发现离散的分段函数不能利用连续函数一样的性质。

然后发现有个条件 \(C\leq 10^6\) 没有用到,比较启发的想到可以更暴力地维护这个过程。

直接记录 \(\text{Mx}_i\) 表示用 \(i\) 元钱能杀死的最大 \(H_jD_j\) 值,对于每种 \(c_i\) 记录最大的 \(h_id_i\),然后调和级数的复杂度暴力刷新。

最后取个前缀 \(\max\),每次二分找到答案即可。

#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 = 3e5 + 10;
const int M = 1e6 + 10;
int n, m, C; LL x[M], Mx[M];

LL read() {
	LL 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(), C = read();
	rep(o, 1, n) {
		LL c = read(), d = read(), h = read();
		x[c] = max(x[c], d * h);
	}
	rep(c, 1, C) {
		LL d = x[c]; if(! d) continue;
		for(int i = c; i <= C; i += c)
			Mx[i] = max(Mx[i], d * (i / c) - 1);
	}
	rep(i, 1, C) Mx[i] = max(Mx[i], Mx[i - 1]);
	Mx[C + 1] = 2e18;
	m = read();
	rep(o, 1, m) {
		LL D = read(), H = read();
		D *= H;
		int ans = lower_bound(Mx + 1, Mx + C + 2, D) - Mx;
		if(ans > C) puts("-1");
		else printf("%d\n", ans);
	}
	return 0;
}

E - Star MST

求美丽的 \(n\) 个点的无向完全图个数。

一张连通无向图是美丽的,当且仅当与 \(1\) 相连的边权和 = 全局最小生成树权值和。

且每条边的权值 \(\in[1,K]\)

发现实际上是 \(1\) 放在上面,下面一排 \(n-1\) 个点,下面的边满足均不小于端点与 \(1\) 的连边大小。

\(f(i,j)\) 表示下面一排 \(i\) 个点,每个点向 \(1\) 的连边值域为 \([1,j]\) 的方案数。

按照 \(j\) 递增的顺序转移,不难得到:

\[\displaystyle f(i,j)=f(i,j-1)+\sum_{k=0}^{i-1} f(k,j-1)\times (K-j+1)^{\text{num}(i,k)} \]

其中 \(\text{num}(i,k)\) 表示新增的边数,DP 是根据新增几条大小恰好为 \(k\) 的节点进行划分的。

显然有 \(\displaystyle \text{num}(i,k)=\frac{(i-k)(i-k-1)}{2}+k(i-k)\),而这些边的值域都为 \([j,K]\),转移就比较显然了。

由于顺序相关,不妨先钦定 \(n-1\) 个点严格按照顺序排序,最后乘上 \((n-1)!\),转移时则需要乘上阶乘的逆元。

当然中途乘上组合数也行,不够优美,实际上乘上逆元就是把多重集的组合分子分母分开维护,这些细节都是平凡的。

#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 = 300;
const int P = 998244353;
int n, k, f[N], fac[N], inv[N];

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;
}

void Pl(int &x, int y) {x += y; if(x >= P) x -= P;}

int main() {
	n = read(), k = read();
	fac[0] = inv[0] = 1;
	rep(i, 1, n) fac[i] = 1LL * i * fac[i - 1] % P;
	inv[n] = Pow(fac[n], P - 2);
	per(i, n - 1, 1) inv[i] = 1LL * (i + 1) * inv[i + 1] % P;
	
	f[0] = 1;
	rep(t, 1, k)
		per(i, n - 1, 0) rep(j, 0, i - 1)
			Pl(f[i], 1LL * f[j] * Pow(k - t + 1, (1LL * (i - j) * j % P + 1LL * (i - j) * (i - j - 1) / 2 % P) % P) % P * inv[i - j] % P);
	printf("%d\n", 1LL * f[n - 1] * fac[n - 1] % P);
	return 0;
}

F - Words on Tree

给定 \(m\) 条树链信息 \((u,v,s)\),其中 \(s\) 是与 \((u,v)\) 路径长度等长的字符串。

要求给每个节点确认字符,使得最终对于每个条件,要么 \((u,v)=s\),要么 \((v,u)=s\)

或者输出无解。

或成为最简单的 F 题,个人认为思维难度不及 D(

其实就是 2-SAT 裸题嘛,每个节点只有至多两种取值。

实现的时候不要傻乎乎的仅在每个节点确定状态,然后讨论来讨论去的。

可以直接把询问也加入状态,根据询问间的矛盾建边,每个节点第一次遍历确定它的 \(0/1\) 状态,代码写起来十分小清新。

#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 = 4e5 + 10;
const int M = N << 2;
int n, q, cnt;
char str[N];
vector<int> T[M];

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 fa[N], dep[N];

void dfs(int u, int Fa) {
	fa[u] = Fa, dep[u] = dep[Fa] + 1;
	for(int v : T[u]) if(v != Fa) dfs(v, u);
}

int id[M][2], frm[N], bac[N], arc[N];
char c[N][2];
vector<int> G[M];

void Add(int u, int ou, int v, int ov) {
	G[id[u][ou]].push_back(id[v][ov]);
	G[id[v][ov ^ 1]].push_back(id[u][ou ^ 1]);
}

void Cov(int u, int v, char *str, int qid) {
	frm[0] = bac[0] = 0;
	while(dep[u] > dep[v]) frm[++ frm[0]] = u, u = fa[u];
	while(dep[v] > dep[u]) bac[++ bac[0]] = v, v = fa[v];
	while(u != v) 
		frm[++ frm[0]] = u, u = fa[u], 
		bac[++ bac[0]] = v, v = fa[v];
	frm[++ frm[0]] = u;
	int t = 0;
	rep(i, 1, frm[0]) arc[++ t] = frm[i];
	per(i, bac[0], 1) arc[++ t] = bac[i];
	rep(i, 1, t) {int u = arc[i]; if(c[u][0] == '#') c[u][0] = str[i], c[u][1] = str[t - i + 1];}
	
	rep(i, 1, t) {
		int u = arc[i];
		rep(o, 0, 1) {
			if(c[u][o] != str[i]) Add(u, o, qid, 1);
			if(c[u][o] != str[t - i + 1]) Add(u, o, qid, 0);
		}
	}
}

int num, tot, top, dfn[M], low[M], stk[M], col[M];

void tarjan(int u) {
	dfn[u] = low[u] = ++ num;
	stk[++ top] = u;
	for(int v : G[u]) {
		if(! dfn[v])
			tarjan(v), low[u] = min(low[u], low[v]);
		else if(! col[v])
			low[u] = min(low[u], dfn[v]); 
	}
	if(dfn[u] == low[u]) {
		int v; tot ++;
		do {
			v = stk[top --];
			col[v] = tot;
		} while(v != u);
	}
}

int main() {
	n = read(), q = read();
	rep(i, 1, n) c[i][0] = c[i][1] = '#';
	rep(i, 1, n + q) id[i][0] = ++ cnt, id[i][1] = ++ cnt;
	rep(i, 2, n) {
		int u = read(), v = read();
		T[u].push_back(v);
		T[v].push_back(u);
	}
	dfs(1, 0);
	rep(i, 1, q) {
		int u = read(), v = read();
		scanf("%s", str + 1);
		Cov(u, v, str, i + n);
	}
	rep(i, 1, cnt) if(! dfn[i]) tarjan(i);
	rep(i, 1, n + q) if(col[id[i][0]] == col[id[i][1]]) {puts("NO"); return 0;}
	puts("YES");
	rep(i, 1, n) {
		char ans = c[i][col[id[i][0]] > col[id[i][1]]];
		if(ans == '#') ans = 'a'; putchar(ans);
	} puts("");
	return 0;
}
posted @ 2022-03-23 20:56  LPF'sBlog  阅读(41)  评论(0编辑  收藏  举报