牛客周赛 Round 154
A 小红的类型转换
涉及知识:无
思路:因为小数点后面全是 \(0\),所以编译器输出的时候会帮我们省略小数点和 \(0\) 的,直接输出即可
Code
点击查看代码
void solve()
{
cin>>x;
cout<<x<<endl;
}
B 小红的矩阵构造
涉及知识:贪心
思路:不难想到第一行正向排列,第二行逆向排列即为最优解
Code
点击查看代码
void solve()
{
cin>>n;
for(int i=1;i<=n;i++)
{
cout<<i<<' ';
}
cout<<endl;
for(int i=n;i>=1;i--)
{
cout<<i<<' ';
}
}
C 小红的序列删除
涉及知识:模拟,贪心
思路:我们可以设最终剩余字符串中 \(U,D,L,R\) 的数量分别为\(C_U,C_D,C_L,C_R\)
最终的坐标为 \(X_0= C_R 一C_L,Y_0= C_U 一C_D\),我们需要最大化的距离为 \(D=|C_R一C_L|+|C_u-C_D|\),做法很显然,就是把这个绝对值式子分情况拆开各自求最优即可
Code
点击查看代码
void solve()
{
cin >> n >> k >> s;
int cnt[256] = {0};
for (char c : s)
{
cnt[c]++;
}
vector<pair<string, string>> a = {
{"LD", "RU"},
{"LU", "RD"},
{"RD", "LU"},
{"RU", "LD"}
};
int ans = -1;
int cnt2[256] = {0};
for (auto str : a)
{
int cnt3[256];
for (int i = 0; i < 256; i++)
cnt3[i] = cnt[i];
int res = k;
for (char c : str.first)
{
int t = min(res, cnt3[c]);
cnt3[c] -= t;
res -= t;
}
for (char c : str.second)
{
int t = min(res, cnt3[c]);
cnt3[c] -= t;
res -= t;
}
x = cnt3['R'] - cnt3['L'];
y = cnt3['U'] - cnt3['D'];
int d = abs(x) + abs(y);
if (d > ans)
{
ans = d;
for (int i = 0; i < 256; i++)
{
cnt2[i] = cnt3[i];
}
}
}
for (char c : s)
{
if (cnt2[c] > 0)
{
cout << c;
cnt2[c]--;
}
}
}
D 小红的01串
涉及知识:思维
思路:先计算出最初的结果,然后再考虑操作对于结果的影响,不难发现,对整个区间的翻转实际上只对边界处产生了影响,所以对于每次操作只需要处理区间左右端点即可
Code
点击查看代码
void solve()
{
int q;
cin >> n >> q >> s;
vi d(n);
int ans = 0;
for (int i = 0; i < n; i++)
{
if (s[i] != s[(i + 1) % n])
{
d[i] = 1;
}
ans += d[i];
}
while (q--)
{
cin >> l >> r;
int li = (l - 1 + n) % n;
int ri = r;
if (li != ri)
{
ans -= d[li];
d[li] ^= 1;
ans += d[li];
ans -= d[ri];
d[ri] ^= 1;
ans += d[ri];
}
cout << ans << endl;
}
}
E 小红的子数组删除
涉及知识:树状数组
思路:因为 \(k\) 是固定的,且要求为连续数组,所以不难想到用滑动窗口,对于不在窗口内的元素,维护他们的中位数即可,对于维护中位数这种 "第 \(k\) 大"问题,不难想到用树状数组维护每移动一位,对左右端点的元素进行增加和删除,判断中位数是否为 \(x\)
Code
点击查看代码
vi tr, b;
int len, ans;
void add(int i, int num)
{
for (; i <= len; i += i & -i)
{
tr[i] += num;
}
}
int que(int num)
{
int idx = 0;
for (int j = 19; j >= 0; j--)
{
int cur = idx + (1 << j);
if (cur <= len && tr[cur] < num)
{
idx = cur;
num -= tr[cur];
}
}
return idx + 1;
}
void check()
{
if (m % 2 == 1)
{
int v = que((m + 1) / 2);
if (b[v - 1] == x)
ans++;
}
else
{
int v1 = que(m / 2);
int v2 = que(m / 2 + 1);
if (b[v1 - 1] + b[v2 - 1] == 2 * x)
ans++;
}
}
void solve()
{
cin >> n >> k >> x;
vi a(n);
for (int i = 0; i < n; i++)
{
cin >> a[i];
}
m = n - k;
if (m == 0)
{
cout << 0 << endl;
return;
}
b = a;
sort(all(b));
b.erase(unique(all(b)), b.end());
len = b.size();
tr.resize(len + 1, 0);
ans = 0;
for (int i = k; i < n; i++)
{
int it = lower_bound(all(b), a[i]) - b.begin() + 1;
add(it, 1);
}
check();
for (int i = 0; i < n - k; i++)
{
int it = lower_bound(all(b), a[i]) - b.begin() + 1;
add(it, 1);
int it2 = lower_bound(all(b), a[i + k]) - b.begin() + 1;
add(it2, -1);
check();
}
cout << ans << endl;
}
F 艾雅法拉的点燃
涉及知识:dp
思路:老样子,贪心是显然不对的,所以考虑 \(dp\),我们设第 \(i\) 个敌人被当做“点燃”主目标的次数为 \(c_i\),那么,第 \(i\) 个敌人受到的总点燃伤害为:总点燃伤害=(自己身上的)+(左边溅射的)+(右边溅射的),如果这个伤害不够把第 \(i\) 个敌人打死,剩下的血只能用普通攻击来补刀,需要补的普攻次数就是 \(a_i\)-总点燃伤害。当我们决定到第 \(i\) 个人时,为了计算第 \(i-1\) 个人有没有死透(是否需要普攻补刀),我们需要知道:第 \(i-2\) 个人的点燃次数(记为 \(u\)),第 \(i-1\) 个人的点燃次数(记为 \(j\)),第 \(i\) 个人的点燃次数(记为 \(k\)),此时,第 \(i-1\) 个人的受到的伤害就彻底确定了:\(2*j+u+k\)。也就可以立刻算出 \(i一1\)需要多少钱补刀了
DP 状态定义:
dp[当前考虑到第 \(i\) 个人][第 \(i-1\) 人的点燃次数 \(j\)][第 \(i\) 人的点燃次数 \(k\)]=此时的最小总花费,通过枚举点燃次数更新 \(dp\) 数组即可
Code
点击查看代码
void solve()
{
cin >> n >> x >> y;
vi a(n + 2, 0);
for (int i = 1; i <= n; i++)
{
cin >> a[i];
}
int dp[2][27][27];
for (int i = 0; i <= 26; i++)
{
for (int j = 0; j <= 26; j++)
{
dp[0][i][j] = INF;
}
}
dp[0][0][0] = 0;
for (int i = 1; i <= n + 1; i++)
{
int cur = i % 2;
int t = (i - 1) % 2;
for (int j = 0; j <= 26; j++)
{
for (int k = 0; k <= 26; k++)
{
dp[cur][j][k] = INF;
}
}
int mx, mx2, mx3;
if (i <= n)
mx = (a[i] + 1) / 2;
else
mx = 0;
if (i - 1 >= 1)
mx2 = (a[i - 1] + 1) / 2;
else
mx2 = 0;
if (i - 2 >= 1)
mx3 = (a[i - 2] + 1) / 2;
else
mx3 = 0;
for (int j = 0; j <= mx2; j++)
{
for (int k = 0; k <= mx; k++)
{
int mi = INF;
for (int u = 0; u <= mx3; u++)
{
if (dp[t][u][j] == INF)
continue;
int c = 2 * j + u + k;
int res = 0;
if (i - 1 >= 1)
{
res = a[i - 1] - c;
if (res < 0)
res = 0;
}
int temp = dp[t][u][j] + res * x;
if (i <= n)
{
temp += y * k;
}
if (temp < mi)
{
mi = temp;
}
}
dp[cur][j][k] = mi;
}
}
}
int ans = INF;
int mx2 = (a[n] + 1) / 2;
for (int j = 0; j <= mx2; j++)
{
if (dp[(n + 1) % 2][j][0] < ans)
{
ans = dp[(n + 1) % 2][j][0];
}
}
cout << ans << endl;
}

浙公网安备 33010602011771号