Educational Codeforces Round 125 (Rated for Div. 2)
C. Bracket Sequence Deletion
prob. : 给一个括号序列,定义一次操作为将最短的满足括号匹配的前缀或者回文前缀删去,问操作次数和剩余的字母长度
idea:
删最短的,则考虑开头两位字母
()
、((
、 ))
这些情况可以直接删去,只有)(
会和后面匹配同时目标是形成回文串,仔细观察发现这个回文串是
)
(
(
(
…
(
)
)(((\dots ()
)(((…() 的形式,则其实直接找下一个)
就可以
btw,我不知道我一上来不动脑子的时候套马拉车为什么还能给我写炸来(你
code
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll inf = 0x3f3f3f3f;
const ll N = 1e6 + 10;
int n;
char s[N];
signed main() {
ll T;
cin >> T;
while (T--) {
scanf("%d %s", &n, s + 1);
int id = 1;
int ans = 0;
while(id <= n) {
if(id + 1 <= n && (!(s[id] == ')' && s[id +1] == '('))) {
id += 2;
ans++;
continue;
}
bool flag = 0;
for(int i = id +2; i <=n; ++ i) {
if(s[i] ==')') {
id = i + 1;
ans++;
flag = 1;
break;
}
}
if(!flag) {
break;
}
}
cout<<ans << " "<< n + 1 - id << endl;
}
return 0;
}
D. For Gamers. By Gamers.
prob. :玩游戏,有n种单位,每个单位有一个单位价格 c i c_i ci,单位能造成的伤害 d i d_i di,单位血量 h i h_i hi,q次询问,每次一个boss,伤害D,血量H,对于每个boss,只能用不超C的钱购买同一种单位,同时要求杀死boss且没有单位被boss杀死的最少花费,伤害为持续伤害,或输出不可能
idea : H s u m d i < h i D \frac{H}{sumd_i} < \frac{h_i}{D} sumdiH<Dhi
⇒ s u m d i × h i > H × D \Rightarrow sumd_i \times h_i > H \times D ⇒sumdi×hi>H×D
要求花费最小,对于从0到C的每个价格c,考虑该花费能造成的最大收益(最大hd)
枚举每个单位的数量更新c
最后是将c变成“花费不大于c的最大价值”
然后二分位置
code
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll inf = 0x3f3f3f3f;
const ll N = 1e6 + 10;
ll c[N], d[N], h[N];
ll damage[N];
signed main() {
ios::sync_with_stdio(false), cin.tie(nullptr), cout.tie(nullptr);
ll n, C;
cin >> n >> C;
for (ll i = 1; i <= n; ++i) {
cin >> c[i] >> d[i] >> h[i];
damage[c[i]] = max(damage[c[i]], d[i] * h[i]);
}
for (ll i = 1; i <= C; ++i) {
for (ll k = 2; k * i <= C; ++k) {
damage[k * i] = max(damage[k * i], damage[i] * k);
}
}
for (ll i = 1; i <= C; ++i) {
damage[i] = max(damage[i], damage[i - 1]);
}
ll q;
cin >> q;
while (q--) {
ll D, H;
cin >> D >> H;
ll num = D*H;
ll l = 1, r = C, ans = -1;
while(l <= r) {
ll mid = (l +r) >> 1;
if(damage[mid] > num) {
ans = mid;
r = mid -1;
}
else l = mid + 1;
}
cout<<ans << " ";
}
return 0;
}
E. Star MST
prob. :给一个n个点无向完全图,每条边有个边权,要求构造每条边边权在w范围内,与节点1相连的所有边构成该图最小生成树的图的个数
idea :
dls题解发好快,dlsyyds
对于每条与1不直接相连的边, 有 e x y ≥ e 1 x , e x y ≥ e 1 y e_{xy} \ge e_{1x} , e_{xy} \ge e_{1y} exy≥e1x,exy≥e1y
考虑构造,对于与1直接相连的边,边权按大小依次构造,
(到这里倒是想到了,后面不会了,我为什么不能"不会了就想想能不能套dp"捏
def d p [ i ] [ j ] dp[i][j] dp[i][j] 为所有边权小于等于i的边,已经和1相连的边为j个
转移考虑边权为i+1 的边,有k个点和1连边的边权是i+1,
选点: ( k n − 1 − j ) (^{n-1-j}_{k}) (kn−1−j);这些边权定了之后,这k个点内部的边边权限制已经定完了(即 ≥ i + 1 \ge i + 1 ≥i+1) , 这k个点和前面j个点的连边的限制也是 ≥ i + 1 \ge i + 1 ≥i+1,这些边的个数总共是 k × ( k − 1 ) 2 + j × k \frac{k \times(k - 1)}{2} + j \times k 2k×(k−1)+j×k
对于每个固定的k对于答案的贡献是 ( k n − 1 − j ) × ( w − i ) k × ( k − 1 ) 2 + j × k (^{n-1-j}_{k}) \times (w - i)^{\frac{k \times(k - 1)}{2} + j \times k} (kn−1−j)×(w−i)2k×(k−1)+j×k
枚举k转移,总体复杂度 O ( n 3 ) O(n^3) O(n3)
code
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll mod = 998244353;
const ll inf = 0x3f3f3f3f;
const ll N = 1e4 + 10;
ll fac[N];
ll invfac[N];
ll invn[N];
void init() {
fac[0] = fac[1] = invfac[0] = invfac[1] = invn[0] = invn[1] = 1;
for (ll i = 2; i < N; ++i) {
fac[i] = fac[i - 1] * i % mod;
invn[i] = (mod - mod / i) * invn[mod % i] % mod;
invfac[i] = invfac[i - 1] * invn[i] % mod;
}
}
ll C(ll up, ll down) {
if (up > down) return 0;
if (up < 0 || down < 0) return 0;
ll res = fac[down];
res = res * invfac[down - up] % mod;
res = res * invfac[up] % mod;
return res;
}
ll qpow(ll x, ll n) {
ll res = 1;
while (n) {
if (n & 1) res = res * x % mod;
x = x * x % mod;
n >>= 1;
}
return res;
}
ll dp[300][300];
signed main() {
ios::sync_with_stdio(false), cin.tie(nullptr), cout.tie(nullptr);
init();
ll n, w;
cin >> n >> w;
dp[0][0] = 1;
for (ll i = 1; i <= w; ++i) {
for (ll j = 0; j < n; ++j) {
for (ll k = 0; k + j < n; ++k) {
ll num = dp[i - 1][j] * C(k, n - 1 - j) % mod * qpow(w - i + 1, k * (k - 1) / 2 + j * k) % mod;
dp[i][k + j] = (dp[i][k + j] + num) % mod;
}
}
}
cout << dp[w][n - 1] << endl;
return 0;
}