Before the Beginning
转载请将本段放在文章开头显眼处,如有二次创作请标明。
原文链接:https://www.codein.icu/cf1398/
前言
补题。ABC都挺水的,D、E有一定难度。
C
求前缀和 $sum(i)$,要求 $sum(i) – sum(j) = i – j$,转化为 $sum(i) – i = sum(j) – j$,用桶记录一下即可。
#include <cstdio>
#include <algorithm>
#include <ctype.h>
const int bufSize = 1e6;
inline char nc()
{
#ifdef DEBUG
return getchar();
#endif
static char buf[bufSize], *p1 = buf, *p2 = buf;
return p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, bufSize, stdin), p1 == p2) ? EOF : *p1++;
}
inline void read(char *s)
{
static char c;
for (; !isdigit(c); c = nc());
for (; isdigit(c); c = nc()) *s++ = c;
*s = '\0';
}
template<typename T>
inline T read(T &r)
{
static char c;
static int flag;
flag = 1, r = 0;
for (c = nc(); !isdigit(c); c = nc()) if (c == '-') flag = -1;
for (; isdigit(c); c = nc()) r = r * 10 + c - 48;
return r *= flag;
}
const int maxn = 1e6 + 100;
int T,n,sum[maxn],cnt[2 * maxn];
char s[maxn];
int main()
{
read(T);
while(T--)
{
read(n);
read(s + 1);
for (int i = 1; i <= n; ++i) sum[i] = sum[i - 1] + s[i] - 48;
for (int i = 1; i <= n; ++i) cnt[sum[i] - i + maxn] = 0;
long long ans = 0;
cnt[0 + maxn] = 1;
for (int i = 1; i <= n; ++i)
{
ans += cnt[sum[i] - i + maxn];
cnt[sum[i] - i + maxn]++;
}
printf("%lld\n", ans);
}
return 0;
}
B
删零是没有意义的,只能创造机会给另一个人一次消去更多的一,因此二人都会贪心找最长的一来删除。
找出所有一段长度,然后模拟一下即可。
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <ctype.h>
const int bufSize = 1e6;
inline char nc()
{
#ifdef DEBUG
return getchar();
#endif
static char buf[bufSize], *p1 = buf, *p2 = buf;
return p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, bufSize, stdin), p1 == p2) ? EOF : *p1++;
}
inline void read(char *s)
{
static char c;
for (; !isdigit(c); c = nc());
for (; isdigit(c); c = nc()) *s++ = c;
*s = '\0';
}
template<typename T>
inline T read(T &r)
{
static char c;
static int flag;
flag = 1, r = 0;
for (c = nc(); !isdigit(c); c = nc()) if (c == '-') flag = -1;
for (; isdigit(c); c = nc()) r = r * 10 + c - 48;
return r *= flag;
}
const int maxn = 110;
int T;
char s[maxn];
int bel[maxn],len[maxn],cnt;
int main()
{
read(T);
while(T--)
{
read(s + 1);
int n = strlen(s + 1);
cnt = 0;
for (int i = 1; i <= n; ++i) bel[i] = 0;
for (int i = 1; i <= n; ++i)
if (s[i] == '1')
{
if (s[i] == s[i - 1]) bel[i] = bel[i - 1], len[bel[i]]++;
else bel[i] = ++cnt, len[cnt] = 1;
}
std::sort(len + 1, len + 1 + cnt);
int flag = 1, ans = 0;
for (int i = cnt; i > 0; i -= 2) ans += len[i];
printf("%d\n", ans);
}
return 0;
}
A
两边之和大于第三边是三角形要求,因此选开头两个数和最后一个数,如果能组成三角形则无解。
#include <cstdio>
#include <algorithm>
#include <ctype.h>
const int bufSize = 1e6;
inline char nc()
{
#ifdef DEBUG
return getchar();
#endif
static char buf[bufSize], *p1 = buf, *p2 = buf;
return p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, bufSize, stdin), p1 == p2) ? EOF : *p1++;
}
inline void read(char *s)
{
static char c;
for (; !isalpha(c); c = nc());
for (; isalpha(c); c = nc()) *s++ = c;
*s = '\0';
}
template<typename T>
inline T read(T &r)
{
static char c;
static int flag;
flag = 1, r = 0;
for (c = nc(); !isdigit(c); c = nc()) if (c == '-') flag = -1;
for (; isdigit(c); c = nc()) r = r * 10 + c - 48;
return r *= flag;
}
const int maxn = 1e5 + 100;
int T,n;
int a[maxn];
int main()
{
read(T);
while(T--)
{
read(n);
for (int i = 1; i <= n; ++i) read(a[i]);
if(a[1] + a[2] > a[n]) puts("-1");
else printf("%d %d %d\n",1,2,n);
}
return 0;
}
D
构造矩形,考虑怎么搭配可以最大。发现大配大一定是最好的,例如有 $r_1 < r_2$ 与 $g_1 < g_2$,若小配小大配大则 $r_1 \times g_1 + r_2 \times g_2$,若小大配则 $r_1 \times g_2 + r_2 \times g_1$,相减得 $r_1 \times (g_1-g_2) + r_2 \times (g_2 – g_1)$,化简得 $(r_2 – r_1) \times (g_2 – g_1) > 0$,说明小小配、大大配一定更优。
既然如此,维护三个大根堆,每次选最大的两个即可。
然而似乎是假的,那么看数据范围小考虑动态规划。
显然三维,$f(i,j,k)$ 代表用了 $i$ 个 $R$,$j$ 个 $G$,$k$ 个 $B$ 时的最大面积,然后转移顺序懒得想,直接乱打个记搜。
#include <algorithm>
#include <cstdio>
#include <cstring>
#include <ctype.h>
const int bufSize = 1e6;
#define DEBUG
inline char nc()
{
#ifdef DEBUG
return getchar();
#endif
static char buf[bufSize], *p1 = buf, *p2 = buf;
return p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, bufSize, stdin), p1 == p2) ? EOF : *p1++;
}
inline void read(char* s)
{
static char c;
for (; !isalpha(c); c = nc());
for (; isalpha(c); c = nc()) *s++ = c;
*s = '\0';
}
template <typename T>
inline T read(T& r)
{
static char c;
static int flag;
flag = 1, r = 0;
for (c = nc(); !isdigit(c); c = nc()) if (c == '-') flag = -1;
for (; isdigit(c); c = nc()) r = r * 10 + c - 48;
return r *= flag;
}
const int maxn = 210;
int R, G, B;
long long f[maxn][maxn][maxn], ans;
int r[maxn], g[maxn], b[maxn];
bool cmp(const int& a, const int& b) { return a > b; }
void dfs(int i, int j, int k)
{
if (f[i][j][k] != -1) return;
f[i][j][k] = 0;
if (i && j) dfs(i - 1, j - 1, k), f[i][j][k] = std::max(f[i][j][k], f[i - 1][j - 1][k] + r[i] * g[j]);
if (i && k) dfs(i - 1, j, k - 1), f[i][j][k] = std::max(f[i][j][k], f[i - 1][j][k - 1] + r[i] * b[k]);
if (j && k) dfs(i, j - 1, k - 1), f[i][j][k] = std::max(f[i][j][k], f[i][j - 1][k - 1] + g[j] * b[k]);
if (i) dfs(i - 1, j, k), f[i][j][k] = std::max(f[i][j][k], f[i - 1][j][k]);
if (j) dfs(i, j - 1, k), f[i][j][k] = std::max(f[i][j][k], f[i][j - 1][k]);
if (k) dfs(i, j, k - 1), f[i][j][k] = std::max(f[i][j][k], f[i][j][k - 1]);
ans = std::max(ans, f[i][j][k]);
}
int main()
{
read(R), read(G), read(B);
for (int i = 1; i <= R; ++i) read(r[i]);
for (int i = 1; i <= G; ++i) read(g[i]);
for (int i = 1; i <= B; ++i) read(b[i]);
std::sort(r + 1, r + 1 + R, cmp);
std::sort(g + 1, g + 1 + G, cmp);
std::sort(b + 1, b + 1 + B, cmp);
memset(f, -1, sizeof(f));
dfs(R, G, B);
printf("%lld\n", ans);
return 0;
}
E
考虑无变化时如何操作,发现每个闪电后面都接上当前伤害最大的法术,可以造成最大的总伤害。也就是总伤害再加上最大的前闪电数量个伤害,即为当前伤害。
显然总伤害开个变量维护即可,问题转化为求前 $k$ 大和,然而特殊情况为前 $k$ 大全是闪电,那么一定有一个闪电没法加倍,而只能让 $k + 1$ 大加倍,特判这种情况,让最小的闪电不加倍即可。
用 set 随便维护一下。实现比较冗杂,交了好多发才过。
#include <cstdio>
#include <set>
#include <algorithm>
#include <ctype.h>
using namespace std;
#define DEBUG
const int bufSize = 1e6;
inline char nc()
{
#ifdef DEBUG
return getchar();
#endif
static char buf[bufSize], *p1 = buf, *p2 = buf;
return p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, bufSize, stdin), p1 == p2) ? EOF : *p1++;
}
inline void read(char *s)
{
static char c;
for (; !isalpha(c); c = nc());
for (; isalpha(c); c = nc()) *s++ = c;
*s = '\0';
}
template<typename T>
inline T read(T &r)
{
static char c;
static int flag;
flag = 1, r = 0;
for (c = nc(); !isdigit(c); c = nc()) if (c == '-') flag = -1;
for (; isdigit(c); c = nc()) r = r * 10 + c - 48;
return r *= flag;
}
int n;
set<pair<int,int>,greater<pair<int,int> > > all;
set<pair<int,int> > sel,selli;
int lightnum;
long long selsum,sum;
inline void maintain()
{
while(sel.size() < lightnum && !all.empty())
{
pair<int,int> t = *all.begin();
all.erase(all.begin());
if (t.second) selli.insert(t);
sel.insert(t), selsum += t.first;
}
while(!sel.empty() && !all.empty())
{
pair<int,int> t1 = *all.begin(),t2 = *sel.begin();
if(t2.first < t1.first || (t2.first == t1.first && t2.second == 1 && t1.second == 0))
{
all.insert(t2),all.erase(t1);
sel.insert(t1),sel.erase(t2);
if(t2.second) selli.erase(t2);
if(t1.second) selli.insert(t1);
selsum -= t2.first,selsum += t1.first;
}
else break;
}
while(sel.size() > lightnum)
{
pair<int,int> t = *sel.begin();
sel.erase(t);
all.insert(t);
if(t.second) selli.erase(t);
selsum -= t.first;
}
}
inline void insert(int x,int light)
{
sum += x;
if(light) ++lightnum;
all.insert(make_pair(x,light));
maintain();
}
inline void del(int x,int light)
{
sum -= x;
if(light) --lightnum;
maintain();
pair<int,int> t = make_pair(x,light);
if(all.find(t) != all.end()) all.erase(t);
else if(sel.find(t) != sel.end())
{
sel.erase(t),selsum -= t.first;
if(t.second) selli.erase(t);
}
maintain();
}
int main()
{
read(n);
while(n--)
{
int type,x;
read(type),read(x);
if(x > 0) insert(x,type);
else del(-x,type);
maintain();
long long ans = sum;
ans += selsum;
if(!selli.empty() && selli.size() == sel.size())
{
ans -= selli.begin()->first;
if(!all.empty()) ans += all.begin()->first;
}
printf("%lld\n",ans);
}
return 0;
}