1. 程式人生 > 其它 >Educational Codeforces Round 125

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