2021 ICPC新疆省赛
2021 ICPC新疆省赛
虽然是第一个10题, 但输在了罚时, zto ljs orz
A - Chino With String
一眼是ac自动机, 然后\(n \leqslant 1e9\) 就是矩阵加速了
但这里并不是乘法运算
路径只能算一个, 所以矩阵外层是取max, 内层是路径权值相加
记得初始化\(ans = -inf\) 这里白wa2发
struct AC {
int tr[N][26], fail[N], tot;
ll ed[N];
void insert(char *s, int k) {
int p = 0;
for (int i = 0; s[i]; p = tr[p][s[i++] - 'a'])
if (!tr[p][s[i] - 'a']) tr[p][s[i] - 'a'] = ++tot;
ed[p] += k;
}
void build() {
queue<int> q;
rep (i, 0, 25) if (tr[0][i]) q.push(tr[0][i]);
while (!q.empty()) {
int p = q.front(); q.pop();
rep (i, 0, 25)
if (tr[p][i]) {
fail[tr[p][i]] = tr[fail[p]][i];
ed[tr[p][i]] += ed[tr[fail[p]][i]]; q.push(tr[p][i]);
}
else tr[p][i] = tr[fail[p]][i];
}
}
} ac;
struct Matrix {
vector<vector<ll>> a;
Matrix(int N = 0, int M = 0) {
vector<vector<ll>>(N, vector<ll>(M, -1e18)).swap(a);
}
Matrix operator*(const Matrix& b) {
Matrix c(a.size(), b.a[0].size());
rep (i, 0, a.size() - 1) rep (j, 0, b.a[0].size() - 1)
rep (k, 0, b.a.size() - 1)
c.a[i][j] = max(c.a[i][j], a[i][k] + b.a[k][j]);
return c;
}
};
Matrix qpow(Matrix a, ll b) {
Matrix c = a; --b;
for (; b; b >>= 1, a = a * a)
if (b & 1) c = c * a;
return c;
}
int n, m, _, k;
char s[205];
ll ans = -2e18;
int main() {
ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
cin >> n >> m;
rep (i, 1, m) cin >> s >> k, ac.insert(s, k);
ac.build(); Matrix a(ac.tot + 1, ac.tot + 1);
rep (i, 0, ac.tot) rep (k, 0, 25)
a.a[i][ac.tr[i][k]] = ac.ed[ac.tr[i][k]];
a = qpow(a, n);
for (auto &x : a.a[0]) ans = max(ans, x);
cout << ans;
return 0;
}
B - Cocktail With Hearthstone
组合数学, 只不过,
- a = n, b = m 没这种情况 0
- a = n, b != 0, 此时 \(C(a, b)\) 只能从 \(C(a - 1, b)\) 转移过来, 所以是 \(\frac{C(a - 1, b)}{2}\)
- a != 0, b = m, 此时 \(C(a, b)\) 只能从 \(C(a, b- 1 )\) 转移过来, 所以是 \(\frac{C(a, b - 1)}{2}\)
- 其他的直接组合数学 \(C(a, b)\)
输出答案的时候别忘了乘比赛的人数也就是\(2^{n + m - a - b}\)
ll facinv[N], inv[N], fac[N], qp[N];
ll C(int n, int m) { return fac[n] * facinv[m] % mod * facinv[n - m] % mod; }
void init(int n) {
fac[0] = fac[1] = facinv[0] = facinv[1] = inv[0] = inv[1] = qp[0] = 1;
qp[1] = 2;
for (int i = 2; i <= n; ++i)
fac[i] = fac[i - 1] * i % mod,
inv[i] = inv[mod % i] * (mod - mod / i) % mod,
facinv[i] = facinv[i - 1] * inv[i] % mod,
qp[i] = qp[i - 1] * 2 % mod;
}
int main() {
ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
cin >> n >> m >> _; init(n + m);
while (_--) {
int a, b; cin >> a >> b;
if (a == n && b == m) cout << "0\n";
else if (a == n && b)
cout << C(n + b - 1, n - 1) * qp[m - b] % mod << '\n';
else if (b == m && a)
cout << C(a + m - 1, a) * qp[n - a] % mod << '\n';
else cout << C(a + b, a) * qp[n + m - a - b] % mod << '\n';
}
return 0;
}
C - Chino With Minimum
罚坐1h, 但就是不会做, 没办法, 看出来是斜率dp了, 可就是不会, 直接罚坐
人急了什么都干得出来, 除了斜率dp还有插头dp
D - Cocktail With Swap
直接暴力并查集, 把能交换的集合合并, 维护个集合的字母数量
int f[N], cnt[N][26];
char s[N];
int main() {
ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
cin >> n >> l >> r >> s + 1;
for (int i = 1; i <= n; ++i) f[i] = i, ++cnt[i][s[i] - 'a'];
if (l == r) {
for (int i = 1; i <= l; ++i) for (int j = i + l; j <= n; j += l)
++cnt[i][s[j] - 'a'], f[j] = i;
} else {
for (int i = 2; i <= n - l; ++i)
++cnt[1][s[i] - 'a'], f[i] = 1;
for (int i = max(n - l + 1, 1 + l); i <= n; ++i)
++cnt[1][s[i] - 'a'], f[i] = 1;
}
for (int i = 1; i <= n; ++i)
for (int j = 0; j < 26; ++j) if (cnt[f[i]][j]) {
cout << char('a' + j);
--cnt[f[i]][j]; break;
}
return 0;
}
E - Is The Order A Rabbit ??
预处理了每天最小值的前缀和, 一直wa, 干脆直接卖的时候\(O(n)\)求, 本身复杂度没变
贪心即可, 维护当前的最大出售价格即可
int a[N][2];
ll ans;
int work(int &i, int day) {
if ((i & 1) && i + 2 <= day) {
int c = a[day >> 1][day & 1] - min(a[i + 1 >> 1][0], a[i + 1 >> 1][1]);
return i += 2, max(0, c);
}
if (i & 1) return ++i, 0;
int c = a[day >> 1][day & 1] - a[i >> 1][1];
return ++i, max(c, 0);
}
int main() {
ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
cin >> n; priority_queue<pair<int, int>> q;
for (int i = 1; i <= n; ++i) {
cin >> a[i][0] >> a[i][1];
q.push({a[i][0], i << 1}); q.push({a[i][1], i << 1 | 1});
}
for (int i = 1; !q.empty();) {
int day = q.top().second; q.pop();
while (i < day) ans += work(i, day);
}
cout << ans;
return 0;
}
F - Chino With Ball
跟那个蚂蚁走独木桥没啥区别, 就是让你按序输出, 排个序就好了
int a[N], b[N];
pair<int, int> rk[N];
int main() {
ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
cin >> n >> k;
for (int i = 1; i <= n; ++i) {
int d; cin >> rk[i].first >> d;
rk[i].second = i;
b[i] = rk[i].first + d * k;
}
sort(b + 1, b + 1 + n);
sort(rk + 1, rk + 1 + n);
for (int i = 1; i <= n; ++i)
a[rk[i].second] = b[i];
for (int i = 1; i <= n; ++i) cout << a[i] << ' ';
return 0;
}
G - Cocktail With Snake
签到
int main() {
ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
for (cin >> _; _; --_) {
ll n, m, k; cin >> n >> m >> k;
ll a = k / n; k %= n;
if (a & 1) {
cout << a + n - k - 1 << '\n';
} else {
cout << a + k << '\n';
}
}
return 0;
}
H - Cocktail With Pony
写这道题的时候是过了就第一了, 没管住手, 改一下交一下,
就是输在了这道题的罚时, 看ljs卡了半天a, 以为他做不出了, 就想题数10压制了, 结果反而输在了罚时
本身是到模拟, 注意到, 🐏不能跨越🐺, 就随便模拟就能过
int n, m, _, k;
int v1, v2, x1, x2;
void print(int x) { cout << x << '\n'; }
bool dfs(int x1, int x2, int dep) {
if (dep & 1) {
if (x2 + v2 <= n) return dfs(x1, x2 + v2, dep + 1);
if (x1 == n - 1) return print(dep), 1;
int c = x2 + v2 - n;
x2 = c & 1 ? n - 1 : n;
return dfs(x1, x2, dep + 1);
}
if (x2 - x1 <= v1) return print(dep), 1;
return dfs(x1 + v1, x2, dep + 1);
}
int main() {
ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
for (cin >> _; _; --_) {
cin >> n >> v1 >> v2 >> x1 >> x2;
if (x1 > x2) x1 = n - x1 + 1, x2 = n - x2 + 1;
dfs(x1, x2, 1);
}
return 0;
}
I - Chino With Mates
正解尺取, 但这复杂度, 写二分更快, 注意上下届, 即可
ll ans;
int main() {
ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
cin >> n >> m; vector<int> a, b;
for (int i = 1; i <= n; ++i) cin >> k, a.emplace_back(k);
for (int i = 1; i <= m; ++i) cin >> k, b.emplace_back(k);
cin >> l >> r;
sort(a.begin(), a.end()); sort(b.begin(), b.end());
for (auto &x : a)
if (x < 0)
ans += upper_bound(b.begin(), b.end(), floor((double)l / x)) - lower_bound(b.begin(), b.end(), ceil(double(r) / x));
else if (x > 0)
ans += upper_bound(b.begin(), b.end(), floor((double)r / x)) - lower_bound(b.begin(), b.end(), ceil(double(l) / x));
else if (l <= 0 && r >= 0) ans += m;
cout << ans;
return 0;
}
J - Cocktail With Not 2b
赛前hhg提了一嘴状压, 我一看, \(log2(1e6) < 20\), 直接写状压去了
然后wa了, 就没在写这个思路了, 赛后发现是变量写错了...
这是状压
const int N = 1e6 + 5;
int n, m, _, k, l, r;
int cnt[N], ans;
vector<int> st[21];
void init(int x) {
for (int k = 0; k < 1 << x; ++k) {
bool f = 1;
for (int i = 1; i < x; ++i) if ((k >> i & 1) && (k >> i - 1 & 1)) {
f = 0; break;
}
if (f) st[x].emplace_back(k);
}
}
int main() {
ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
cin >> n;
for (int i = 1; i <= n; ++i) cin >> m, ++cnt[m];
for (int i = 1; i <= 1e6; ++i) if (cnt[i] && ((i & 1) || !cnt[i >> 1])) {
int len = 0;
for (int j = i; j <= 1e6 && cnt[j]; j <<= 1) ++len;
if (st[len].empty()) init(len);
int res = n;
for (auto &x : st[len]) {
int cur = 0;
for (int j = 0; j < len; ++j) if (!(x >> j & 1))
cur += cnt[i << j];
res = min(res, cur);
}
ans += res;
}
cout << ans;
return 0;
}
然后正解是直接dp完事, 按dp来想确实是到签到题
int n, m, _, k, l, r;
int cnt[N], ans, f[N][2];
int main() {
ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
cin >> n;
for (int i = 1; i <= n; ++i) cin >> m, ++cnt[m];
for (int i = 1; i <= 1e6; ++i) {
if (i & 1) {
f[i][0] = cnt[i];
f[i][1] = 0;
} else {
f[i][0] = min(f[i >> 1][0], f[i >> 1][1]) + cnt[i];
f[i][1] = f[i >> 1][0];
}
}
for (int i = 1e6; i * 2 > 1e6; --i) ans += min(f[i][0], f[i][1]);
cout << ans;
return 0;
}
K - Chino With C language
签到, 而且并不是题目说说的\(memcpy\)并不会覆盖
\(memcpy\)会检测从左或从右赋值, 总有一个方向不会覆盖, 这个\(copy\)并不会发生覆盖
当然按题意写就完事了, 签到
char s[N], t[N];
int main() {
ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
cin >> n >> s + 1 >> l >> r >> k;
memcpy(t + 1, s + 1, n * sizeof(char));
memmove(t + r, t + l, k * sizeof(char));
for (int i = 0; i < k; ++i) s[r + i] = s[l + i];
cout << s + 1 << '\n' << t + 1;
return 0;
}
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步