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