CF 1555
CF1555
A:
推公式题,每一个都是 \(2.5\) 的时间,那么我们尽量用 \(10\) 。
如果是偶数,那么直接可以凑出来,如果是奇数,就 \(+1\) 变成偶数来凑,再考虑特殊情况即可。
#include<bits/stdc++.h>
using namespace std;
#define int long long
int T,n;
signed main()
{
cin>>T;
while(T--){
cin>>n;
if(n<6) cout<<15<<endl;
else cout<<(n+(n%2?1:0))/2*5<<endl;
}
// system("pause");
return 0;
}
B:
算剩下来的空间左右和上下是否能放下,如果可以,答案就是挪动最少的次数。
枚举最小的情况即可,比较简单。
#include<bits/stdc++.h>
using namespace std;
int T,W,H,X1,Y1,X2,Y2,w,h;
int main()
{
cin>>T;
while(T--){
cin>>W>>H>>X1>>Y1>>X2>>Y2>>w>>h;
int ans=0x3f3f3f3f,nowx=X2-X1,nowy=Y2-Y1;
if(w+nowx<=W) ans=min(ans,min(max(0,w-X1),max(0,X2-W+w)));
if(h+nowy<=H) ans=min(ans,min(max(0,h-Y1),max(0,Y2-H+h)));
if(ans==0x3f3f3f3f) puts("-1");
else printf("%.6lf\n",(double)ans);
}
// system("pause");
return 0;
}
C:
可以把题意汇总成一句话:
枚举 \(A\) 在哪里下去,然后 \(B\) 要不直接下去,要不在最后一格才下去
递推即可。
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=1e5+1e3;
int T,m;
int a[N],b[N],ans[N],sum1,sum2,answer;
signed main(){
cin>>T;
while(T--){
memset(ans,0,sizeof ans);
answer=0x3f3f3f3f,sum1=0,sum2=0;
//sum1记录在i位置下去时上方红色部分总和
//sum2记录在i位置下去时下方蓝色部分总和
cin>>m;
for(int i=1;i<=m;i++) cin>>a[i],sum1+=a[i];//提前处理好第一排总和
for(int i=1;i<=m;i++){
cin>>b[i];
sum1-=a[i];//由上面图可知,红色部分从i+1开始
ans[i]=max(sum1,sum2);//取蓝色部分和红色部分中的大值(Bob的聪明)
answer=min(ans[i],answer);//取所有蓝色部分和红色部分中的大值中的最小值(Alice的聪明)
sum2+=b[i];// 由上面图可知,蓝色部分到i-1结束
}
printf("%lld\n",answer);//最后输出answer,完事
}
return 0;
}
D:
既然只有三个字符,那么回文的情况我们都可以枚举出来。
发现不回文的字符串只能使一个以前三个字符组成的循环,只有 \(6\) 种情况。
对于每次询问,我们枚举这 \(6\) 种方式,取最小值即可。
#include<bits/stdc++.h>
using namespace std;
const int N=2e5+5;
int n,m,sum[7][N];
string s;
void work(char x , char y , char z , int t){
for(int i = 0; i < n; i++){
sum[t][i + 1] += sum[t][i];
if(i % 3 == 0 && s[i] != x) sum[t][i + 1]++;
if(i % 3 == 1 && s[i] != y) sum[t][i + 1]++;
if(i % 3 == 2 && s[i] != z) sum[t][i + 1]++;
}
}
int main(){
scanf("%d%d",&n,&m);
cin >> s;
work('a' , 'b' , 'c' , 1);
work('b' , 'a' , 'c' , 2);
work('c' , 'a' , 'b' , 3);
work('c' , 'b' , 'a' , 4);
work('a' , 'c' , 'b' , 5);
work('b' , 'c' , 'a' , 6);
for(int i = 1,l,r,ans=0x3f3f3f3f; i <= m; i++){
scanf("%d%d",&l,&r); ans=0x3f3f3f3f;
for(int j = 1; j <= 6; j++)
ans = min(ans , sum[j][r] - sum[j][l - 1]);
printf("%d\n",ans);
}
// system("pause");
return 0;
}
E:
一句话题意:最小化所选线段权值的最大值与最小值之差
我们很显然就可以用双指针做。
双指针的过程中,加入一条线段 \([l,r]\),我们在线段树上将区间 \([l,r−1]\) 加一,删除该线段时就在线段树上将区间 \([l,r−1]\) 减一,判断区间是否被全部覆盖就查询 \([1,m−1]\) 的最小值是否大于零即可。
至于为什么是 \([l,r−1]\),我们可以发现题目要求我们选的线段必须要有重合部分,转化之后就变成将区间 \([1,m−1]\) 覆盖即可,会好做一些。
#include<bits/stdc++.h>
using namespace std;
using namespace std;
inline int read(){
int x=0; char c=getchar();
while(!isdigit(c)) c=getchar();
while(isdigit(c)) x=x*10+(c^'0'),c=getchar();
return x;
}
const int N=3e5+10,M=1e6+10;
struct Node{
int l,r,val,tag;
}t[M*4];
inline void pushup(int x){
t[x].val=min(t[x<<1].val,t[x<<1|1].val);
}
inline void pushdown(int x){
if(t[x].tag){
t[x<<1].val+=t[x].tag,t[x<<1].tag+=t[x].tag;
t[x<<1|1].val+=t[x].tag,t[x<<1|1].tag+=t[x].tag;
t[x].tag=0;
}
}
void build(int x,int l,int r){
t[x].l=l,t[x].r=r,t[x].val=t[x].tag=0;
if(l==r) return;
int mid=l+r>>1;
build(x<<1,l,mid),build(x<<1|1,mid+1,r);
}
void modify(int x,int l,int r,int v){
if(l<=t[x].l&&r>=t[x].r){
t[x].val+=v,t[x].tag+=v;
return;
}
pushdown(x);
int mid=t[x].l+t[x].r>>1;
if(l<=mid) modify(x<<1,l,r,v);
if(r>mid) modify(x<<1|1,l,r,v);
pushup(x);
}
int n,m;
struct Segment{
int l,r,w;
bool operator<(const Segment &x)const{
return w<x.w;
}
}s[N];
int main(){
n=read(),m=read();
for(int i=1;i<=n;i++) s[i].l=read(),s[i].r=read()-1,s[i].w=read();
sort(s+1,s+n+1);
build(1,1,m-1);
int ans=0x3f3f3f3f;
for(int l=1,r=0;l<=n;l++){
while(!t[1].val){
if(++r>n){
printf("%d\n",ans);
// system("pause");
return 0;
}
modify(1,s[r].l,s[r].r,1);
}
ans=min(ans,s[r].w-s[l].w);
modify(1,s[l].l,s[l].r,-1);
}
printf("%d\n",ans);
// system("pause");
return 0;
}
不关注的有难了😠😠😠https://b23.tv/hoXKV9

浙公网安备 33010602011771号