Educational Codeforces Round 61
虽然是场virtual 但这是第一次在cf的比赛里进前100 好开心
【我知道我很菜qvq
比赛链接
A
如果有")("就把它们排在一起放在"(("和“))”中间 然后保证"((","))"一样多就好啦
#include <cstdlib>
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <cstring>
#include <queue>
#include <vector>
#include <set>
using namespace std;
const int N = 3e5 + 5;
int c1, c2, c3, c4;
int main(){
scanf("%d%d%d%d", &c1, &c2, &c3, &c4);
bool flag = 0;
if(c1 == c4) flag = 1;
if(c3 && (!c1)) flag = 0;
printf("%d", flag);
return 0;
}
C
有m个区间 从所有区间中删掉两个
求最大覆盖面积
\(n, m <= 5000\)
有一点技巧的暴力?
先暴力处理每个数被覆盖了几次
然后枚举一个区间i 把它的贡献去掉
然后统计现在被覆盖次数为1的前缀和
再枚举一个区间j 用前缀和直接计算最优值
最后恢复区间i的贡献
#include <cstdlib>
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <cstring>
#include <queue>
#include <vector>
#include <set>
using namespace std;
const int N = 5005;
int n, m;
int cnt[N], one[N], l[N], r[N];
int ans;
int main(){
scanf("%d%d", &n, &m);
for(int i = 1; i <= m; ++i){
scanf("%d%d", &l[i], &r[i]);
for(int j = l[i]; j <= r[i]; ++j) ++cnt[j];
}
for(int i = 1; i <= m; ++i){
for(int j = l[i]; j <= r[i]; ++j) --cnt[j];
int res = n;
for(int j = 1; j <= n; ++j) one[j] = one[j - 1] + (cnt[j] == 1);
for(int j = 1; j <= m; ++j) if(j != i){
res = min(res, one[r[j]] - one[l[j] - 1]);
}
for(int j = 1; j <= n; ++j) res += (cnt[j] == 0);
for(int j = l[i]; j <= r[i]; ++j) ++cnt[j];
ans = max(ans, n - res);
}
printf("%d", ans);
return 0;
}
D
有 n 个笔记本,初始电量 ai,每分钟会耗电 bi,比赛时长k分钟
求最小的充电器的每分钟充电量,使得每个电脑在比赛期间电量非负
二分答案+优先队列貌似会TLE
check的时候对每个电脑求处最晚要充电的位置就好啦
#include <cstdlib>
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <cstring>
#include <queue>
using namespace std;
const int N = 3e5 + 5;
const long long inf = 1e15;
int n, m, cnt[N];
long long a[N], b[N];
inline bool check(long long W){
memset(cnt, 0, sizeof(cnt));
int tot = 0;
for(int i = 1; i <= n; ++i) if(b[i]){
long long cur = a[i];
while(cur < (m - 1) * b[i]){
if(cur / b[i] < m) ++cnt[cur / b[i]];
if(tot > m) return 0;
++tot, cur += W;
}
}
for(int i = 0; i < m; ++i){
if(cnt[i] > i + 1) return 0;
cnt[i + 1] += cnt[i];
}
return 1;
}
int main(){
scanf("%d%d", &n, &m);
for(int i = 1; i <= n; ++i) scanf("%lld", &a[i]);
for(int i = 1; i <= n; ++i) scanf("%lld", &b[i]);
long long l = 0, r = inf, mid;
while(l < r){
mid = l + ((r - l) >> 1);
if(check(mid)) r = mid;
else l = mid + 1;
}
printf("%lld\n", l == inf ? -1 : l);
return 0;
}
E
这道题题目是Knapsack 然鹅我没有看
一波乱搞 最大取法减6搜索 然后过了?【雾
贴一下正解好了。。
orzFlyppy_White
//乱搞。。
#include <cstdlib>
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <cstring>
#include <queue>
#include <vector>
#include <map>
using namespace std;
long long W, c[10], ans;
map<long long, long long> mp;
void dfs(long long cur, long long w){
//if(mp[w] > cur) return; else mp[w] = cur;
if(!cur){ans = max(ans, W - w); return ;}
long long d = min(c[cur], w / cur);
for(long long i = max(0ll, d - 6ll); i <= d; ++i){
dfs(cur - 1, w - i * cur);
}
}
int main(){
scanf("%lld\n", &W);
for(int i = 1; i <= 8; ++i) scanf("%lld", &c[i]);
dfs(8ll, W);
printf("%lld", ans);
return 0;
}
F
给定一个长度为n,只含小写字母的串
每次删除一段含有一样字母区间
求最少删多少次删完整个字符串
\(n \leq 500\)
区间dp
#include <cstdlib>
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <cstring>
#include <queue>
#include <vector>
#include <map>
using namespace std;
const int N = 505;
int n;
char str[N];
int f[N][N];
int main(){
scanf("%d%s", &n, str + 1);
for(int i = 1; i <= n; ++i) f[i][i] = 1;
for(int i = 2; i <= n; ++i){
for(int l = 1, r = i; r <= n; ++l, ++r){
if(str[l] == str[r]) f[l][r] = f[l + 1][r - 1] + 1;
else f[l][r] = min(f[l + 1][r], f[l][r - 1]) + 1;
for(int k = l; k <= r; ++k){
f[l][r] = min(f[l][r], f[l][k] + f[k][r] - 1);
}//这里是把f[l][r] = f[l][k] + f[k + 1][r]和i, j, k三位相同时f[l][r] = str[l][k] + f[k][r] - 1合并了
}
}
printf("%d", f[1][n]);
return 0;
}
G
本题题解转自The_Virtuoso
题目大意:给定一个序列\(a\),定义它的最长贪心严格上升子序列为\(b\)满足若\(a_{i}\)在\(b\)中则\(a_{i}\)之后第一个比它大的也在\(b\)中。给出一个数\(k\),求出所有长度为\(k\)的子区间的最长贪心严格上升子序列。
考虑如果选取一个数之后一定会选取它之后第一个比它大的数,那么我们将每个数与它右边第一个比他大的数连边,这样我们就得到了一个森林,再建立一个虚拟节点并将森林中所有根都连向他就得到了一棵树。对于整个序列来说选取了一个点就会选取这个点在树上到根路径上的所有点,而整个序列的答案就是每个点深度的最大值。现在考虑一个子区间的答案,当区间右端点右移时,新加入区间的这个数会对原区间中比这个数小的数的答案\(+1\)也就是将这个数在树上的子树中所有点的答案\(+1\)(这些答案\(+1\)的所有点中虽然包括区间之前的数但显然这些数的答案不会比区间内数的答案更大,最多只会与最大值相同),同样当区间左端点右移时,就将这个数在树上的子树中所有点的答案\(-1\)来确保区间之前的数的答案不会比区间中数的答案更优。我们对整棵树的\(dfs\)序维护线段树,每次询问只需要分别将左右端点右移然后在线段树上区间修改并查询全局最大值即可。至于每个数右边第一个大于它的数只需要维护一个单调递减的单调栈然后每次弹栈时将栈顶与当前数连边即可。
#include <cstdlib>
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <cstring>
#include <queue>
using namespace std;
const int N = 1e6 + 5;
const int inf = 0x3f3f3f3f;
int fa[N], rec[N], a[N];
int n, m, stk[N], top, dfn[N], low[N], tim;
struct Edge{int v, next;}edge[N];
int head[N], esize;
inline void addedge(int x, int y){edge[++esize] = (Edge){y, head[x]}, head[x] = esize;}
struct Seg{
int w[N << 2], tag[N << 2];
inline void pushdown(int x){
w[x << 1] += tag[x], w[x << 1 | 1] += tag[x],
tag[x << 1] += tag[x], tag[x << 1 | 1] += tag[x], tag[x] = 0;
}
inline void update(int x){
w[x] = max(w[x << 1], w[x << 1 | 1]);
}
void ins(int rt, int l, int r, int x, int y, int d){
if(x <= l && r <= y){
w[rt] += d, tag[rt] += d;
return ;
}
pushdown(rt);
int mid = l + ((r - l) >> 1);
if(x <= mid) ins(rt << 1, l, mid, x, y, d);
if(y > mid) ins(rt << 1 | 1, mid + 1, r, x, y, d);
update(rt);
}
}seg;
void dfs(int x){
dfn[x] = ++tim;
for(int i = head[x], vv; ~i; i = edge[i].next){
vv = edge[i].v; dfs(vv);
}
low[x] = tim;
}
int main(){
memset(head, -1, sizeof(head));
scanf("%d%d", &n, &m);
for(int i = 1; i <= n; ++i) scanf("%d", &a[i]);
a[n + 1] = inf, stk[0] = n + 1;
for(int i = n; i >= 1; --i){
while(top && a[stk[top]] <= a[i]) --top;
fa[i] = stk[top], addedge(stk[top], i), stk[++top] = i;
}
dfs(n + 1);
for(int i = 1; i <= m; ++i){
seg.ins(1, 1, n + 1, dfn[i], low[i], 1);
}
printf("%d ", seg.w[1]);
for(int i = m + 1; i <= n; ++i){
seg.ins(1, 1, n + 1, dfn[i - m], low[i - m], -1);
seg.ins(1, 1, n + 1, dfn[i], low[i], 1);
printf("%d ", seg.w[1]);
}
return 0;
}