Educational Codeforces Round 95 (Rated for Div. 2)
A
数学题
输入 a b k
题目上来只给你 一个s
两个操作
一个 s 可以换成 a 个 s
b 个 s 可以换 一个 c
一个 t = 一个 s + 一个 c(该步骤不算操作)
问要 k 个 t 需要 多少 次操作
有点像进制转化,昨天一直wa是因为 k * b 个 s 变成 k 个 c 是 k 步,而不是 b 步,
还有就是不是每次 + a, 是每次 + (a - 1),最后虽然说看出来了,可是太慢了
队友之间打比赛突然发现是 1 + 1 + 1 < 1 的太难了,互相讨论,然后自己还被打扰,思路断了就想不出来了,哎,难受,互相之间的理解还是不够,一起打的比赛太少了
我一直说 k * b 个 s 变成 k 个 c 是 b 步,没有人说我是错的,哎
#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
using namespace std;
int main(){
int t;
scanf("%d",&t);
while(t --){
long long a, b, k;
scanf("%lld%lld%lld",&a,&b,&k);
long long ans = k;
long long l = (b+1)*k - 1;
ans += l/(a-1);
if(l % (a - 1) != 0)
ans ++;
printf("%lld\n",ans);
}
return 0;
}
B
给两个数组,
数组A是一个正常的数组
数组B是一个由零一组成的数组,
数组B中为零的位置对应于数组A的位置是可以进行交换的,
数组B中为一的位置对应于数组A的位置是不能动的
设 k 为数组A变化完的数组的前缀和数组的最后一个为负数的点的位置
如果前缀和中没有为负数的点,那么k = 0,
找到 k 的最小值
麻烦的地方在于有一些点是锁住的,
应该是把该数组的可动位置从大到小排序,
问的是前缀和数组 < 0 的最后一个位置 的 最小值
因为是前缀和数组,如果是从小到大的话,
先是一堆负数,前缀和数组的负数特别大,然后慢慢加正数,负数慢慢接近0,这样k是接近数组结尾的
而如果是从大到小的话
先是一些正数,前缀和也是正的,在是一些负数,前缀和数组的值慢慢变小,有可能没有负值,那么k = 0,
其实如果所有数加一起是负数的话,按照原数组相加就行了,因为无论怎么变换顺序,前缀和数组的最后一项也是负数,
#include <stdio.h>
#include <bits/stdc++.h>
using namespace std;
#define Inf 1000000000000000
int main(){
int t;
cin>>t;
while(t--){
int n;
cin>>n;
vector<int> a(n),l(n);
long long all = 0;
for(int i = 0;i < n;i ++) scanf("%d",&a[i]), all += a[i];
for(int i = 0;i < n;i ++) scanf("%d",&l[i]);
if(all < 0){//可有可无
for(int i = 0;i < n;i ++){
if(i!=0)cout<<' ';
cout<<a[i];
}
cout<<endl;
continue;
}
vector<int> L;
for(int i = 0;i < n;i ++){
if(l[i]==0){
L.push_back(a[i]);
}
}
sort(L.begin(),L.end());
for(int i = 0;i < n;i ++){
if(l[i]==0){
a[i] = L.back();
L.pop_back();
}
}
for(int i = 0;i < n;i ++){
if(i!=0)cout<<' ';
cout<<a[i];
}
cout<<endl;
}
return 0;
}
C
其实c还挺麻烦的
如果for一遍判断01去模拟的话是不对的,情况太多了,无法讨论完
比如几个数据
0 0 0 1 1
0 1 0 0 0 1 1
0 1 0 1 0 1 1
1 0 0 0 1 1
可是其实可以简化
除第一位后如果出现了三个连续的1那么必然会 cnt ++
其他的都不会出现 cnt 增加的情况
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
int n;
int z[200010];
int main(){
int _;
scanf("%d",&_);
while(_--){
scanf("%d",&n);
for(int i = 0;i < n;i ++)
scanf("%d",&z[i]);
int all = 0;
int t = 0;
if(z[0])
all ++;
for(int i = 1;i < n;i ++){
if(z[i] && z[i + 1] && z[i + 2] && (i + 2) < n ){
all ++;
i += 2;
}
}
printf("%d\n",all);
}
return 0;
}
还有一种方法就是DP
其实DP的方向还是挺容易想到的,
因为没有特判,要求是按照规定进行找到最值,中间是连续的
可是转移方程怎们写呢
两个数组互相之间更新
一个代表自己 (m) 的数组,下标表示该位置是自己时朋友花费最少的值,
一个代表朋友 ( f ) 的数组,下标表示该位置是朋友时朋友花费最少的值,
然后用f[i]就可以更新m[i + 1]和m[i + 2],用m[i + 1]就可以更新f[i + 2]和f[i + 3],用m[i + 2]就可以更新f[i + 3]和f[i + 4]。
#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
using namespace std;
const int N = 200200;
int n;
int dp[N][2];
int z[N];
int main(){
int _;
scanf("%d",&_);
while(_--){
memset(dp, 0x3f3f3f, sizeof dp);
scanf("%d",&n);
for(int i = 0;i < n;i ++) scanf("%d",&z[i]);
if(z[0]){
dp[0][0] = 1;
dp[1][1] = 1;
dp[2][1] = 1;
}
else{
dp[0][0] = 0;
dp[1][1] = 0;
dp[2][1] = 0;
}
if(z[1]){
dp[1][0] = dp[0][0] + 1;
dp[3][1] = min(dp[1][0], dp[3][1]);
dp[2][1] = min(dp[1][0], dp[2][1]);
}
else{
dp[1][0] = dp[0][0];
dp[3][1] = dp[1][0];
dp[2][1] = min(dp[1][0], dp[2][1]);
}
for(int i = 1;i < n;i ++){
dp[i + 1][0] = min(dp[i + 1][0], dp[i][1] + z[i + 1]);
dp[i + 2][0] = min(dp[i + 2][0], dp[i][1] + z[i + 1] + z[i + 2]);
dp[i + 1][1] = min(dp[i + 1][1], dp[i][0]);
dp[i + 2][1] = min(dp[i + 2][1], dp[i][0]);
}
printf("%d\n",min(dp[n-1][0], dp[n-1][1]));
}
return 0;
}
D
上来一发T
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>
#include <set>
using namespace std;
int n, m;
set <int> s;
set <int> ::iterator it;
set <int> ::iterator itr;
set <int> ::iterator ite;
int main(){
int l;
scanf("%d%d", &n, &m);
for(int i = 0;i < n;i ++)
scanf("%d", &l), s.insert(l);
it = s.begin();
ite = s.end();
ite --;
int ans = *ite - *it;
itr = it;
itr ++;
for( ; it != ite ; it ++, itr ++){
ans = min(ans, *it - *s.begin() + *ite - *itr);
}
printf("%d\n",ans);
while(m --){
int a, b;
scanf("%d%d",&a,&b);
if(a) s.insert(b);
else s.erase(b);
if(s.begin() == s.end()){
printf("0\n");
continue;
}
it = s.begin();
ite = s.end();
ite --;
int ans = *ite - *it;
itr = it;
itr ++;
for( ; it != ite ; it ++, itr ++){
ans = min(ans, *it - *s.begin() + *ite - *itr);
}
printf("%d\n",ans);
}
return 0;
}
然后想可以优化为找到中值,然后继续左右相减,可是不对
比如 1 2 4 6 8 … 50 52 100
其中52 应该是向左走,而不是向右走
想错了
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>
#include <set>
using namespace std;
int n, m;
set <int> s;
set <int> ::iterator it;
set <int> ::iterator it2;
set <int> ::iterator it2r;
set <int> ::iterator ite;
int main(){
int l;
scanf("%d%d", &n, &m);
for(int i = 0;i < n;i ++)
scanf("%d", &l), s.insert(l);
it = s.begin();
ite = s.end(), ite --;
int ans = *ite - *it;
it2 = s.lower_bound((*ite - *it + 1) / 2);
it2 --;
it2r = ++ it2;
it2 --;
ans = min(ans, *it2 - *it + *ite - *it2r);
printf("$$$$$$ %d\n",ans);
while(m --){
int a, b;
scanf("%d%d",&a,&b);
if(a) s.insert(b);
else s.erase(b);
if(s.begin() == s.end()){
printf("0\n");
continue;
}
it = s.begin();
ite = s.end(), ite --;
int ans = *ite - *it;
it2 = s.lower_bound((*ite - *it + 1) / 2);
it2 --;
it2r = ++ it2;
it2 --;
ans = min(ans, *it2 - *it + *ite - *it2r);
printf("$$$$$$ %d\n",ans);
}
return 0;
}
也就是顺便看了一篇二分函数的博客
multiset 的 删除操作:
ms.erase(2);//把所有的 2 删除
ms.erase(ms.find(2));//只删除一个 2
其实这个的思路巧妙,代码也不好写,这个题还是挺有难度的
代码重写一次,还改过无数次,分类讨论的各个分支,输入条件的边界情况
得亏知道思路后打了一下,没想到这么麻烦
#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>
#include <set>
using namespace std;
int n, m;
int main(){
set <int> s;
set <int> :: iterator se;
set <int> :: iterator sb;
set <int> :: iterator sl;
set <int> :: iterator sr;
multiset <int> ms;
multiset <int> :: iterator mse;
multiset <int> :: iterator msb;
multiset <int> :: iterator mst;
scanf("%d%d", &n, &m);
for(int i = 0 ; i < n;i ++){
int l;
scanf("%d",&l);
s.insert(l);
}
if(n == 1)//得特判,没有 mse
printf("0\n");
else{
sb = s.begin();
se = s.end();
sr = sb, sr ++;
for(sl = sb;sr != se;sl ++, sr ++){
ms.insert(*sr - *sl);
}
se --;
mse = ms.end(), mse --;
int ans = *se - *sb - *mse;
printf("%d\n",ans);
}
while(m --){
int a, b;
scanf("%d%d",&a,&b);
if(a){
int sll = s.size();
if(sll == 0){
s.insert(b);
}
else if(sll == 1){
if(*s.begin() > b) ms.insert(*s.begin() - b);
else ms.insert(b - *s.begin());
s.insert(b);
}
else{
sb = s.begin();
se = s.end(), se --;
if(b > *sb && b < *se){
sr = s.upper_bound(b);
sl = sr;
sl --;
ms.erase(ms.find(*sr - *sl));//只删除一个,后面也一样
ms.insert(*sr - b);
ms.insert(b - *sl);
s.insert(b);
}
else if(b <= *sb){
ms.insert(*sb - b);
s.insert(b);
}
else{
ms.insert(b - *se);
s.insert(b);
}
}
}
else{
int sll = s.size();
if(sll == 1 || sll == 2){
s.erase(b);
ms.clear();
}
else{
sb = s.begin();
se = s.end(), se --;
if(*se == b){
s.erase(b);
sl = s.end();
sl --;
mst = ms.find(b - *sl);
ms.erase(mst);
}
else if(*sb == b){
s.erase(b);
sl = s.begin();
mst = ms.find(*sl - b);
ms.erase(mst);
}
else{
s.erase(b);
sr = s.upper_bound(b);
sl = sr;
sl --;
ms.insert(*sr - *sl);
mst = ms.find(*sr - b);
ms.erase(mst);
mst = ms.find(b - *sl);
ms.erase(mst);
}
}
}
if(s.size() <= 2){
printf("0\n");
continue;
}
sb = s.begin();
se = s.end(), se --;
mse = ms.end(), mse --;
int ans = *se - *sb - *mse;
printf("%d\n",ans);
}
return 0;
}