教育场被教育了
A. Morning Sandwich
题意:
制作一个汉堡,汉堡必须是两面包夹芝士或者汉堡肉的形式(可以有很多层),现有b片面包,c片芝士和h片汉堡肉,问最多能制作多少层的汉堡
题解:
#define _CRT_SECURE_NO_WARNINGS
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<vector>
using namespace std;
typedef long long LL;
typedef unsigned long long ULL;
typedef pair<int, int> PII;
const LL N = 1e5 + 10, MOD = 998244353, INF = 0x3f3f3f3f;
void solve()
{
int n, a, b;
scanf("%d%d%d", &n, &a, &b);
a += b;
printf("%d\n", min(n - 1, a) * 2 + 1);
}
int main()
{
int T = 1;
scanf("%d", &T);
while (T--)
{
solve();
}
return 0;
}
B. Monsters
题意:
有n只怪物,每只怪的生命为ai,你只有一种攻击方式,锁定一名敌方当前生命值最高的单位并对他造成k点伤害(若有多个生命值相同的单位优先锁定编号小的那个),问你击杀怪物的顺序
题解:
优先攻击生命最高的单位,在某次攻击后敌方生命会全部小于等于k(全部变为(ai-1)%k+1),在这之后你将开始击杀并会优先锁定生命值高的,因此击杀顺序是优先击杀(ai-1)%k+1较大的,可以用map<int,vector>来存下然后反着输出。
#define _CRT_SECURE_NO_WARNINGS
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<vector>
#include<map>
using namespace std;
typedef long long LL;
typedef unsigned long long ULL;
typedef pair<int, int> PII;
const LL N = 1e5 + 10, MOD = 998244353, INF = 0x3f3f3f3f;
map<int, vector<int> >mp;
void solve()
{
mp.clear();
int n, k;
scanf("%d%d", &n, &k);
for (int i = 1, x; i <= n; ++i)
{
scanf("%d", &x);
mp[(x - 1) % k + 1].push_back(i);
}
auto it = mp.rbegin();
while (it != mp.rend())
{
for (auto i : it->second)
printf("%d ", i);
++it;
}
printf("\n");
}
int main()
{
int T = 1;
scanf("%d", &T);
while (T--)
{
solve();
}
return 0;
}
C. Binary String Copying
题意:
给定一长度为n的01串,以该01串为原串生成m个新串,生成的字符串为对原串下标l到r的区间进行排序之后的新串,问这些新串中有多少个是不同的(去重之后还剩几个)
题解:
可以记录排序之后新串相对于原串的变化并用set去重。先处理原串中'1'的数量的前缀和以及对于每个位置来说他后面最近的0的位置,每次更改后区间l到r内的1都会挤到区间的后面,那我们就可以记录修改之后的情况:修改之后在位置i之前有连续k个1,存入{i,k}。特别的,当排序的区间内的1本来就都在末尾的情况排序之后与原串没有变化,这时候需要特殊处理一下(例如存入{0,0})
#define _CRT_SECURE_NO_WARNINGS
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<vector>
#include<set>
using namespace std;
typedef long long LL;
typedef unsigned long long ULL;
typedef pair<int, int> PII;
const LL N = 2e5 + 10, MOD = 998244353, INF = 0x3f3f3f3f;
char ch[N];
int ne[N], cnt[N];
set<PII>st;
void solve()
{
st.clear();
int n, m;
scanf("%d%d%s", &n, &m, ch + 1);
ne[n + 1] = n + 1;
for (int i = 1, j = n; i <= n; ++i, --j)
{
cnt[i] = cnt[i - 1] + ch[i] - '0';
if (ch[j] == '1')
ne[j] = ne[j + 1];
else
ne[j] = j;
}
for (int i = 1; i <= m; ++i)
{
int l, r;
scanf("%d%d", &l, &r);
int c = cnt[r] - cnt[l - 1];
if (cnt[r] - cnt[r - c] == c)
{
st.insert({ 0,0 });
continue;
}
c += ne[r + 1] - r - 1;
st.insert({ ne[r + 1] ,c });
}
printf("%d\n", st.size());
}
int main()
{
int T = 1;
scanf("%d", &T);
while (T--)
{
solve();
}
return 0;
}
D. Array Painting
题意:
你有一个长度为n的数组a(0<=ai<=2),刚开始他们都是蓝色,你的目标是把他们都染成红色,你有一下两种操作:1、选择一个蓝色的点并把它染成红色,2、选择一个ai>0的红色的点并选择一个与它相邻的蓝色点,将其染成红色并使ai减一。求最少操作次数。
题解:
贪心。对于一个存在2的连续的只有1或2的区间就相当于一个2,对于一个连续的只存在1的区间就相当于一个1(应该挺好想的)。只要先把题目给的数组优化一下从左往右贪心即可(优化后整个数组就不存在连续的1或2,所有的1或2都被0分割,相对起来好处理)。
#define _CRT_SECURE_NO_WARNINGS
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<vector>
#include<set>
using namespace std;
typedef long long LL;
typedef unsigned long long ULL;
typedef pair<int, int> PII;
const LL N = 2e5 + 10, MOD = 998244353, INF = 0x3f3f3f3f;
int a[N];
vector<int>v;
void solve()
{
v.clear();
int n, ans = 0;
scanf("%d", &n);
for (int i = 1; i <= n; ++i)
scanf("%d", &a[i]);
for (int i = 1; i <= n; ++i)
{
if (a[i])
{
int f = 0;
while (i <= n && a[i])
{
if (a[i] == 2)f = 1;
++i;
}
if (f)v.push_back(2);
else v.push_back(1);
--i;
}
else
v.push_back(0);
}
for (int i = 0; i < v.size(); ++i)
{
//先染色i+1然后让后面的数进行2操作染色i
if (i + 1 < v.size() && v[i + 1])--v[i + 1];
else ++ans;//直接染色i
if (v[i])++i;//染色i之后对i进行2操作染色i+1
}
printf("%d\n", ans);
}
int main()
{
int T = 1;
//scanf("%d", &T);
while (T--)
{
solve();
}
return 0;
}
E. Max to the Right of Min
不会写的题
题意:
给出一个1到n的排列,定义minposl,r为区间l到r范围内最小值的下表,maxposl,r同理,求给出排列中存在多少个区间满足minposl,r<maxposl,r。
不会解:
不会写题解,我的评价是看这篇:Educational Codeforces Round 152 (Rated for Div. 2) A - F - 知乎 (zhihu.com) 这个思路感觉挺好的
代码抄了jiangly的,加了一点没啥用的注释
//基本CV了jiangly的E题代码
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<vector>
using namespace std;
typedef long long LL;
typedef unsigned long long ULL;
typedef pair<int, int> PII;
const LL N = 1e6 + 10, MOD = 998244353, INF = 0x3f3f3f3f;
int a[N], lmin[N], rmin[N], lmax[N], rmax[N];
int erfen(vector<int>&v,int l,int r, int t)
{
while (l < r)
{
int mid = l + r >> 1;
if (rmin[v[mid]] > t)
l = mid + 1;
else
r = mid;
}
return l - 1;
}
void solve()
{
int n;
scanf("%d", &n);
for (int i = 1; i <= n; ++i)
scanf("%d", &a[i]);
//单调栈处理每个点左/右比它小/大的第一个点的下标(不存在则为0/n+1)
vector<int>smin, smax;
for (int i = 1; i <= n; ++i)
{
while (smin.size() && a[smin.back()] > a[i])
{
rmin[smin.back()] = i;
smin.pop_back();
}
if (smin.size())
lmin[i] = smin.back();
smin.push_back(i);
while (smax.size() && a[smax.back()] < a[i])
{
rmax[smax.back()] = i;
smax.pop_back();
}
if (smax.size())
lmax[i] = smax.back();
smax.push_back(i);
}
while (smin.size())
{
rmin[smin.back()] = n + 1;
smin.pop_back();
}
while (smax.size())
{
rmax[smax.back()] = n + 1;
smax.pop_back();
}
//以上都是单调栈处理
LL ans = 0;
vector<int>v{ 0 };//单调栈维护左边比ai小的下标集合
vector<LL>sum{ 0LL };//前缀和
for (int i = 1; i <= n; ++i)
{
//考虑计数以i为最大值下表的方案
while (v.size() > 1 && a[v.back()] > a[i])
{
v.pop_back();
sum.pop_back();
}
//首先左选点最多到lmax[i],右选点最多到rmax[i](集合中不能有元素比ai大)
//minpos的选择最多到单调栈中的第l个↓
int l = upper_bound(v.begin(), v.end(), lmax[i]) - v.begin();
if (l < v.size())
{
//大致分成三部分做
//1、minpos为v[l]时的方案数
ans += 1LL * (v[l] - max(v[l - 1], lmax[i])) * (min(rmax[i], rmin[v[l]]) - i);
//二分找到最后一个rmin[v[m]]>rmax[i]
int m = erfen(v, l + 1, v.size(), rmax[i]);
//2、左选点在v[l+1]到v[m]时右选点最大都为rmax[i]
ans += 1LL * (v[m] - v[l]) * (rmax[i] - i);
//3、左选点在v[m+1]到lmin[i]时右选点最大为各自的rmin[v[j]],这里使用前缀和处理
//Σ(v[j]-lmin[v[j]])*(rmin[v[j]]-i),(这里m+1<=j<=v.size()-1)
//=Σ(v[j]-lmin[v[j]])*rmin[v[j]]-Σ(v[j]-lmin[v[j]])*i
//=(sum.back()-sum[m]) - (lmin[i]-v[m])*i;
ans -= 1LL * (lmin[i] - v[m]) * i;
ans += sum.back() - sum[m];
}
//计算前缀和,因为对于每个i会分别减去多算的部分所以这里是直接*rmin[i]而不是(rmin[i]-几)
sum.push_back(sum.back() + 1LL * (i - lmin[i]) * rmin[i]);
v.push_back(i);
}
printf("%lld\n", ans);
}
int main()
{
int T = 1;
//scanf("%d", &T);
while (T--)
{
solve();
}
return 0;
}