"蔚来杯"2022牛客暑期多校训练营5 题解
C. Bit Transmission
记录每一位上的YES/NO个数,发现若有一位 ① 没被问到 或者 ② YES个数等于NO个数,则直接输出-1。
然后枚举每一位询问次数超过3的位置,若发现同时存在YES/NO,则这个位置会产生错误。如果发现两处及以上错误,则不满足题目条件,直接输出-1。
如果只有一个错误或者没有错误,那么我们直接输出即可。
#pragma GCC optimize(2)
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int MAXN = 2e6 + 5;
int cnt[MAXN], check[MAXN], ans[MAXN], n;
void Solve() {
for(int i = 1; i <= n; i++) cnt[i] = check[i] = 0, ans[i] = -1;
bool cur = 1;
for(int i = 1; i <= 3 * n; i++) {
int id, flag; string S;
cin >> id >> S; ++id;
if(id > n) continue;
if(S == "YES") flag = 1;
else if(S == "NO") flag = -1;
cnt[id]++;
check[id] += flag;
}
if(!cur) return void(cout << "-1\n");
for(int i = 1; i <= n; i++) if(!cnt[i] || !check[i]) return void(cout << "-1\n");
int wa = 0;
for(int i = 1; i <= n; i++) {
if(cnt[i] >= 3) {
if( cnt[i] - abs(check[i]) > 2 ) return void(cout << "-1\n");
if( abs(check[i]) < cnt[i] ) {
if(wa) return void(cout << "-1\n");
else wa = i;
}
}
}
for(int i = 1; i <= n; i++) ans[i] = (check[i] > 0 ? 1 : 0);
for(int i = 1; i <= n; i++) cout << ans[i] << ""; cout << "\n";
}
signed main()
{
while(cin >> n) Solve();
return 0;
}
D. Birds in the tree
分别枚举叶子节点为0,1的情况。
设 \(dp[1][u], \ dp[2][u]\) 分别表示不限制/限制节点 \(u\) 的类别的方案数,设 \(v\) 是 \(u\) 的儿子, \(l \in \{0,1\}\) ,有
\[当S[u] = l时,dp[1][u] = dp[2][u] = \prod (dp[1][v] + 1) \\
当S[u] ≠ l时,dp[1][u] = \prod (dp[1][v] + 1) - 1, dp[2][u] = dp[1][u] - \sum dp[1][v]
\]
#pragma GCC optimize(2)
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int MAXN = 3e5 + 5;
const int MOD = 1e9 + 7;
int n, dp[3][MAXN];
string S;
vector<int> G[MAXN];
void DFS(int u, int f, int l) {
dp[1][u] = 1; int sum = 0;
for(auto v : G[u]) {
if(v == f) continue;
DFS(v, u, l);
dp[1][u] = dp[1][u] * (dp[1][v] + 1) % MOD;
sum = (sum + dp[1][v]) % MOD;
}
if(S[u] - '0' == l) dp[2][u] = dp[1][u];
else dp[1][u]--, dp[2][u] = (dp[1][u] - sum + MOD) % MOD;
}
signed main()
{
cin >> n >> S; S = " " + S;
for(int i = 1; i < n; i++) {
int u, v; cin >> u >> v;
G[u].push_back(v);
G[v].push_back(u);
}
int ans = 0;
DFS(1, 0, 1);
for(int i = 1; i <= n; i++) ans = (ans + dp[2][i]) % MOD, dp[1][i] = dp[2][i] = 0;
DFS(1, 0, 0);
for(int i = 1; i <= n; i++) ans = (ans + dp[2][i]) % MOD, dp[1][i] = dp[2][i] = 0;
cout << ans << "\n";
return 0;
}
E. Fraction Game
设 \(st[i][j][l]\) 表示三角形顶点坐标 \((i,j)\),边长 \(2^l\) 时的最大值。转移方程为:
\[st[i][j][l] = \max \{st[i][j][l-1], \max_{y=j}^{j+2^{l-1}}\{st[i+2^{l-1}][y][l-1]\}\}
\]
图示如洛谷原题第一篇题解。
注意到时间空间都有可能爆炸。
在空间上,我们发现第三维的 \(l\) 只和 \(l-1\) 有关,可以将第三维优化掉,空间复杂度 \(O(n^2)\) 。
在时间上,我们发现固定 \(i\) 从小到大枚举 \(j\) 的时候,所得max的区间 \([j,j+2^{l-1}]\) 会向右滚动,利用单调队列优化即可,时间复杂度 \(O(n^2 \log k)\)
//#pragma GCC optimize(2)
#include<bits/stdc++.h>
#define int long long
#define pii pair<int, int>
#define fi first
#define se second
using namespace std;
const int MAXN = 1005;
int n, K;
pii st[2][MAXN][MAXN];
deque<int> Q;
pii operator + (const pii& a, const pii& b) {
pii res = {0, 1};
res.se = a.se * b.se;
res.fi = a.se * b.fi + b.se * a.fi;
int g = __gcd(res.fi, res.se);
res.fi /= g, res.se /= g;
return res;
}
pii operator - (const pii& a, const pii& b) {
pii res = {0, 1};
res.se = a.se * b.se;
res.fi = b.se * a.fi - b.fi * a.se;
int g = __gcd(res.fi, res.se);
res.fi /= g, res.se /= g;
if(res.fi * res.se < 0) {
res.fi = abs(res.fi);
res.se = abs(res.se);
res.fi *= -1;
}
return res;
}
bool operator < (const pii& a, const pii& b) {
pii res = a - b;
if(res.fi < 0) return 1;
else return 0;
}
bool operator > (const pii& a, const pii& b) {
pii res = a - b;
if(res.fi > 0) return 1;
else return 0;
}
pii max(pii a, pii b) {
pii res = a - b;
if(res.fi > 0) return a;
else return b;
}
pii min(pii a, pii b) {
pii res = a - b;
if(res.fi < 0) return a;
else return b;
}
signed main()
{
scanf("%lld%lld", &n, &K);
for(int i = 1; i <= n; i++) for(int j = 1; j <= i; j++) scanf("%lld/%lld", &st[0][i][j].fi, &st[0][i][j].se);
int l;
for(l = 1; (1 << l) <= K; l++) {
for(int i = 1; i + (1 << l) - 1 <= n; i++) {
int x = i + (1 << l - 1);
for(int j = 1; j <= (1 << l - 1); j++) {
while(!Q.empty() && st[0][x][ Q.back() ] < st[0][x][j]) Q.pop_back();
Q.push_back(j);
}
for(int j = 1; j <= i; j++) {
int y = j + (1 << l - 1);
while(!Q.empty() && Q.front() < j) Q.pop_front();
while(!Q.empty() && st[0][x][ Q.back() ] < st[0][x][y]) Q.pop_back();
Q.push_back(y);
st[1][i][j] = max(st[0][i][j], st[0][x][ Q.front() ]);
}
while(!Q.empty()) Q.pop_back();
}
for(int i = 1; i <= n; i++) for(int j = 1; j <= i; j++) st[0][i][j] = st[1][i][j], st[1][i][j] = {0, 1};
}
l--;
if((1 << l) < K) {
for(int i = 1; i + K - 1 <= n; i++) {
int x = i + K - (1 << l);
for(int j = 1; j <= K - (1 << l); j++) {
while(!Q.empty() && st[0][x][ Q.back() ] < st[0][x][j]) Q.pop_back();
Q.push_back(j);
}
for(int j = 1; j <= i; j++) {
int y = j + K - (1 << l);
while(!Q.empty() && Q.front() < j) Q.pop_front();
while(!Q.empty() && st[0][x][ Q.back() ] < st[0][x][y]) Q.pop_back();
Q.push_back(y);
st[1][i][j] = max(st[0][i][j], st[0][x][ Q.front() ]);
}
while(!Q.empty()) Q.pop_back();
}
for(int i = 1; i <= n; i++) {
for(int j = 1; j <= i; j++) st[0][i][j] = st[1][i][j], st[1][i][j] = {0, 1};
}
}
pii ans = {0, 1};
for(int i = 1; i + K - 1 <= n; i++) {
for(int j = 1; j <= i; j++) ans = (ans + st[0][i][j]);
}
printf("%lld/%lld", ans.fi, ans.se);
return 0;
}
G. KFC Crazy Thursday
manacher求出每个下标 \(i\) 的最大拓展长度 \(f[i]\),对于每个 \([i, i+f[i]]\) 中的特定字符,其在里面出现的位置都可作为一个回文串的结尾,前缀和维护即可。
#pragma GCC optimize(2)
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int MAXN = 2e6 + 5;
const int BASE = 27;
const int MOD = 1e9 + 7;
char T[MAXN], S[MAXN];
int sum[MAXN][28], n, f[MAXN];
void Manacher() {
T[0] = '#', T[1] = '$';
for(int i = 1; i <= n; i++) T[i << 1] = S[i], T[i << 1 | 1] = '$';
n = n << 1 | 1;
for(int i = 0; i <= n; i++) S[i] = T[i];
for(int i = 0; i <= n; i++) f[i] = 0;
int maxR = 0, pos = 0;
for(int i = 1; i <= n; i++) {
if(i < maxR) f[i] = min(f[pos * 2 - i], maxR - i);
while(i - f[i] - 1 > 0 && i + f[i] + 1 <= n && S[i + f[i] + 1] == S[i - f[i] - 1]) f[i]++;
if(i + f[i] > maxR) maxR = i + f[i], pos = i;
}
}
void Solve() {
Manacher();
int ans1 = 0, ans2 = 0, ans3 = 0;
for(int i = 1; i <= n; i++) {
for(int j = 0; j < 26; j++) sum[i][j] = sum[i - 1][j];
if(S[i] >= 'a' && S[i] <= 'z') sum[i][ S[i] - 'a' ]++;
}
for(int i = 1; i <= n; i++) {
ans1 += sum[ i + f[i] ]['k' - 'a'] - sum[ i - 1 ]['k' - 'a'];
ans2 += sum[ i + f[i] ]['f' - 'a'] - sum[ i - 1 ]['f' - 'a'];
ans3 += sum[ i + f[i] ]['c' - 'a'] - sum[ i - 1 ]['c' - 'a'];
}
cout << ans1 << " " << ans2 << " " << ans3 << "\n";
}
signed main()
{
while(cin >> n) {
cin >> S + 1;
Solve();
}
return 0;
}

浙公网安备 33010602011771号