Codeforces Round 612 题解
[比赛链接]
http://codeforces.com/contest/1286
[题解]
\(A\)
不妨令 \(dp_{i , j , 0 / 1}\) 表示用了 \(i\) 个奇数 , \(j\) 个偶数 , 上一位奇数 / 偶数的最小值。
考虑当前位置奇数的情况 , 有
\(dp_{i , j , 0} = min\{dp_{i - 1 , j , 0} , dp_{i - 1 , j , 1} + 1\}\)。
偶数同理。
时间复杂度 \(O(N ^ 3)\)
\(B\)
最简单的方式是构造一个排列。
从根开始 \(DFS\) , 暴力合并子树 , 并将当前节点插入集合。
时间复杂度 \(O(N)\)
\(C\)
首先不难发现 :
\(1.\) 把一个子串中字符乱序相当于给定了出现次数
\(2.\) 对于同一个询问得到的两个子串,只有按照它们的长度才能把它们区分开。
先来考虑简单版本的问题 , 考虑 \([1 , N]\) 所有长度为 \(i\) 的子串和 \([2 , N]\) 所有长度为 \(i\) 的子串 , 将各个字符做差 , 就得到了 \([1 , i]\) 每个字符出现的次数 , 进一步将其与 \([1 , i - 1]\) 做差 , 就得到了第 \(i\) 位的字符。
这样的子串个数是 \({N \choose 2} + {N + 1 \choose 2} = N ^ 2\)。
接着考虑困难版。
不难发现 , 当 \(i\le\lceil\frac N2\rceil\) 时 , 原串每个字符贡献了 \(min\{j , N - i + 1 , i\}\) 次。而长度变为 \(i - 1\) 时 , 贡献变化当且仅当 \(\min(j,N-j+1)\ge i\) , 也就是 \(j\in[i , N - i + 1]\)。
于是长度为 \(i\) 的所有子串之和减去长度为 \(i − 1\) 的所有子串之和即为子串 \([i , N − i + 1]\) ,可以直观理解成两个梯形的面积相减。
然后再进行差分 , 就得到了 \(i\) 和 \((N - i + 1)\) 位置上的字符可能是哪两个。
这时就只需要知道前一半和后一半了。
子串个数 \(\binom{n+1}2+(\frac n2)^2<0.75(n+1)^2\)。
\(D\)
首先注意到一次碰撞必然发生在相邻的球之间 (相向而行、往左追击,往右追击)。
把这 \(2(N - 1)\) 种情况预处理出来 , 并按照时间从小到大排序。
这样 , 第 \(i\) 个碰撞作为第一次碰撞的概率即为前 \((i - 1)\) 次都没发生 , 而第 \(i\) 次碰撞发生了的概率。
那么每次相当于禁掉一个方向 , 而对于 \(i\) 禁了三个方向。
记 \(dp_{i , 0 / 1}\) 表示前 \(i\) 个位置 , 上一个位置是向左 / 右的概率。
直接转移是 \(O(N ^ 2)\) 的。 但可以用线段树维护矩阵优化。详见 [代码]
时间复杂度 \(O(NlogN)\)
\(E\)
考虑维护 \(Border\) 集合.
在添加字符时不妨顺带维护 \(fail\) 数组 , 还可以记一个 \(anc_{i}\) 表示 \(fail\) 树上最近的祖先满足 \(s_{x + 1} \neq s_{fail_{i} + 1}\).
这样便可以在 \(O(1)\) 的时间内找出一个不合法的 \(Border\) 进行删除.
既然要计算贡献 , 那么额外维护一个单调栈计算后缀最值即可.
时间复杂度 \(O(NlogN)\) (均摊分析)
\(F\)
每次使用操作二时 , 不妨在 \(i\) 和 \(j\) 间连一条无向边 , 这样就得到了图 \(G\).
如果 \(G\) 中有环 , 那么还不如对环上每个点进行操作一.
故 \(G\) 是一片森林. 而要做的便是最大化森林中树的棵数.
考虑判断一个集合是否能连成一棵树?
假设每次边的两个端点都是减去 \(x\) , 任选一点为根 , 那么每个点对根的贡献之和其深度有关.
故满足条件当且仅当奇数深度点权和 = 偶数深度点权和.
下面考虑可以给某些点 \(+1\) , 那么也就是 \(|奇数点权和 - 偶数点权和| < |S|\) 且奇偶性相同.
直接枚举子集可以做到 \(O(3 ^ N)\).
不难发现可以枚举大的那一半 , 时间复杂度 \(O((1 + \sqrt{2})^N)\)
预处理完了以后便可以状压 \(DP\) 了 , 总时间复杂度 \(O((1 + \sqrt{2})^N + \log{N} \cdot N^2 \cdot 2^N)\)
[代码]
\(A\)
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
#define rep(i , l , r) for (int i = (l); i < (r); ++i)
const int MN = 105;
int N , A[MN] , dp[MN][MN][MN];
int main() {
scanf("%d" , &N);
for (int i = 1; i <= N; ++i) scanf("%d" , &A[i]);
memset(dp , 0x3f , sizeof(dp));
dp[0][0][0] = dp[0][0][1] = 0;
for (int i = 1; i <= N; ++i)
for (int j = 0; j <= i; ++j) {
if (A[i] % 2 || !A[i]) dp[i][j][1] = min(dp[i - 1][j][0] + 1 , dp[i - 1][j][1]);
if (A[i] % 2 == 0 && j) dp[i][j][0] = min(dp[i - 1][j - 1][0] , dp[i - 1][j - 1][1] + 1);
}
printf("%d\n" , min(dp[N][N / 2][0] , dp[N][N / 2][1]));
return 0;
}
\(B\)
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
#define rep(i , l , r) for (int i = (l); i < (r); ++i)
const int MN = 2005;
int N , c[MN] , ans[MN];
vector < int > e[MN] , res;
#define sz(a) (int)(a.size())
inline int solve(int x) {
int ans = x > 0;
for (auto y : e[x]) ans += solve(y);
if (x) {
if (c[x] >= ans) {
printf("NO\n");
exit(0);
}
res.insert(res.begin() + c[x] , x);
}
return ans;
}
int main() {
scanf("%d" , &N);
for (int i = 1; i <= N; ++i) {
int par;
scanf("%d%d" , &par , &c[i]);
e[par].emplace_back(i);
}
solve(0);
printf("YES\n");
for (int i = 0; i < sz(res); ++i)
ans[res[i]] = i + 1;
for (int i = 1; i <= N; ++i)
printf("%d " , ans[i]);
return 0;
}
\(C\)
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
int n;
auto query(int l , int r) {
multiset < string > s;
cout << "? " << l + 1 << " " << r << "\n";
for (int i = 0; i < (r - l) * (r - l + 1) / 2; ++i) {
string t;
cin >> t;
sort(t.begin() , t.end());
s.insert(t);
}
return s;
}
int main() {
cin >> n;
if (n <= 3) {
string s;
for (int i = 0; i < n; ++i)
s += *query(i , i + 1).begin();
cout << "! " << s << "\n";
return 0;
}
int m = (n + 1) / 2;
auto s = query(0 , m) , t = query(1 , m);
for (auto &&i : t)
s.erase(s.find(i));
vector < string > pre(s.begin() , s.end());
sort(pre.begin() , pre.end() , [&](const auto &lhs , const auto &rhs) {
return lhs.length() < rhs.length();
});
string hf = pre[0];
for (int i = 1; i < m; ++i) {
vector < int > cnt(26);
for (char c : pre[i])
++cnt[c - 'a'];
for (char c : pre[i - 1])
--cnt[c - 'a'];
for (int j = 0; j < 26; ++j)
if (cnt[j] == 1)
hf += 'a' + j;
}
string ans(n , 'a');
auto f = query(0 , n);
vector < vector < string > > sl(n);
for (auto s : f)
sl[s.size() - 1].emplace_back(s);
for (int i = 0; i < m; ++i)
ans[i] = hf[i];
for (int i = 0; i < n / 2; ++i) {
vector < int > cnt(26);
for (auto &t : sl[n - i - 2])
for (char c : t)
++cnt[c - 'a'];
for (int j = 0; j <= i; ++j)
cnt[ans[j] - 'a'] -= j + 1;
for (int j = n - i; j < n; ++j)
cnt[ans[j] - 'a'] -= n - j;
for (int j = 0; j < 26; ++j)
if (cnt[j] % (i + 2) != 0)
ans[n - 1 - i] = 'a' + j;
}
cout << "! " << ans << "\n";
return 0;
}
\(D\)
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int MN = 1e5 + 5 , mod = 998244353;
inline int qPow(int a , int b) {
int c = 1;
for (; b; b >>= 1 , a = 1ll * a * a % mod) if (b & 1) c = 1ll * c * a % mod;
return c;
}
const int inv = qPow(100 , mod - 2);
struct node {
int x , a , b , s , v;
} C[MN * 2];
int N;
bool ban[MN][2][2];
int idx[MN << 2] , val[MN << 2][2][2] , foo[MN][2] , md[MN << 2] , A[MN] , B[MN];
inline void inc(int &x , int y) {
x = x + y < mod ? x + y : x + y - mod;
}
inline bool cmp(node a , node b) {
return 1LL * a.s * b.v < 1LL * a.v * b.s;
}
inline void pushup(int now) {
for (int i = 0; i < 2; ++i) {
for (int j = 0; j < 2; ++j) {
val[now][i][j] = 0;
for (int x = 0; x < 2; ++x)
for (int y = 0; y < 2; ++y) {
if (!ban[md[now]][x][y])
inc(val[now][i][j] , 1ll * val[now << 1][i][x] * val[now << 1 | 1][y][j] % mod);
}
}
}
}
inline void build(int now , int l , int r) {
if (l == r) {
val[now][0][0] = foo[l][0];
val[now][1][1] = foo[l][1];
return;
}
int mid = l + r >> 1;
md[now] = mid , idx[mid] = now;
build(now << 1 , l , mid) , build(now << 1 | 1 , mid + 1 , r);
pushup(now);
}
int main() {
scanf("%d" , &N);
for (int i = 1; i <= N; ++i) {
scanf("%d%d%d" , &A[i] , &B[i] , &foo[i][1]);
foo[i][1] = 1LL * foo[i][1] * inv % mod;
foo[i][0] = (1 - foo[i][1] + mod) % mod;
}
build(1 , 1 , N);
int M = 0;
for (int i = 2; i <= N; ++i) {
C[M++] = (node) {i - 1 , 1 , 0 , A[i] - A[i - 1] , B[i] + B[i - 1]};
if (B[i] < B[i - 1]) C[M++] = (node) {i - 1 , 1 , 1 , A[i] - A[i - 1] , B[i - 1] - B[i]};
if (B[i] > B[i - 1]) C[M++] = (node) {i - 1 , 0 , 0 , A[i] - A[i - 1] , B[i] - B[i - 1]};
}
sort(C , C + M , cmp);
int ans = 0 , lst = 1;
for (int i = 0; i < M; ++i) {
ban[C[i].x][C[i].a][C[i].b] = 1;
for (int now = idx[C[i].x]; now; now >>= 1) pushup(now);
int cur = (((val[1][0][0] + val[1][0][1]) % mod) + ((val[1][1][0] + val[1][1][1]) % mod)) % mod;
inc(ans , 1ll * C[i].s * qPow(C[i].v , mod - 2) % mod * (((lst - cur + mod) % mod) % mod) % mod);
lst = cur;
}
printf("%d\n" , ans);
return 0;
}
\(E\)
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
#define rep(i , l , r) for (int i = (l); i < (r); ++i)
const int MN = 6e5 + 5, M = 1 << 30;
const LL mod = 1e18;
map<int , int> cnt;
int N , w[MN], fail[MN], anc[MN], st[MN], top;
LL res;
char s[MN];
struct BigInt {
LL a , b;
inline void operator += (const LL &x) {
if ((b += x) >= mod) b -= mod , ++a;
}
inline void output() {
if (a) printf("%lld%018lld\n" , a , b);
else printf("%lld\n" , b);
}
inline int val(int P) {
return (a * (mod % P) + b) % P;
}
} ans;
int main() {
scanf("%d" , &N);
for (int i = 1 , j = 0; i <= N; ++i) {
scanf("%s%d" , s + i , &w[i]);
s[i] = (s[i] - 'a' + ans.val(26)) % 26 + 'a'; w[i] ^= ans.val(M) & (M - 1);
anc[i - 1] = s[fail[i - 1] + 1] == s[i] ? anc[fail[i - 1]] : fail[i - 1];
while (j && s[j + 1] != s[i]) j = fail[j];
if (i > 1 && s[j + 1] == s[i]) ++j;
fail[i] = j;
for (int now = i - 1 , val; now;)
if (s[now + 1] == s[i]) now = anc[now];
else val = w[*lower_bound(st + 1 , st + top + 1 , i - now)] , (--cnt[val] == 0) ? cnt.erase(val) : 0 , res -= val , now = fail[now];
while (top && w[st[top]] > w[i]) --top;
st[++top] = i;
if (s[i] == s[1]) ++cnt[w[i]] , res += w[i];
for (auto it = cnt.upper_bound(w[i]) , nxt = it; it != cnt.end(); ++it , cnt.erase(nxt) , nxt = it)
cnt[w[i]] += it -> second , res -= 1ll * it -> second * (it -> first - w[i]);
ans += res; ans.output();
}
return 0;
}
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int MN = 21;
int N , Log[1 << MN] , chk[1 << MN] , f[1 << MN] , siz[1 << MN];
LL A[MN] , sum[1 << MN];
inline void chkmax(int &x , int y) {
x = max(x , y);
}
int main() {
scanf("%d" , &N);
for (int i = 0; i < N; ++i) {
scanf("%lld" , &A[i]); Log[1 << i] = i;
if (!A[i]) --i , --N;
}
for (int i = 1; i < (1 << N); ++i)
sum[i] = sum[i ^ (i & (-i))] + A[Log[i & (-i)]];
for (int i = 0; i < (1 << N); ++i) {
siz[i] = siz[i >> 1] + (i & 1);
if (siz[i] == 1) continue;
for (int j = (i - 1) & i; (2 * j >= i) && j; j = (j - 1) & i)
if (abs(sum[j] - sum[i ^ j]) < siz[i] && ((abs(sum[j] - sum[i ^ j]) & 1) != (siz[i] & 1))) {
chk[i] = true;
break;
}
}
for (int s = 1; s < (1 << N); ++s) {
if (chk[s]) {
int k = ((1 << N) - 1) ^ s; f[s] = max(f[s] , 1);
for (int t = k; t; t = (t - 1) & k) chkmax(f[s | t] , f[t] + 1);
}
}
int ans = 0;
for (int s = 0; s < (1 << N); ++s) chkmax(ans , f[s]);
printf("%d\n" , N - ans);
return 0;
}