CSP-S2024题解游记
CSP-S2024
- 题解
- T1 决斗(duel)
- 题意
- 思路
- 代码
- T2 超速检测(detect)
- 题意
- 思路
- 二分
- 贪心
- 代码
题解
说些有意思的,听说有考生把J组poker的文件操作写成了joker……
好华丽的自我介绍
T1 决斗(duel)
P11231决斗
题意
一个数列,大的数最多能够使一个小的数被删掉,问最后剩几个数字?
思路
首先,如果一个数能够被删掉,那么比它小的数一定能也能被删掉,而大一点的数也许能留着删别的,所以先删最小的数;
接着,在没有用掉删除次数的数中,我们选择最小的(同理,更大的也许能删最小的删不了的数);
如果我们将即将被删的怪兽集合称为“死亡名单”
也就是说,我们将这个数列从小到大排序,遍历能力的值,每到一个数,我们就检测“死亡名单”中有多少能够被删,尽量删,然后能力值为这个数的怪兽成为“死亡名单”中新的成员。(过河拆桥法 )
累计被删除的怪兽数量即可。
代码
赛时代码,b表示被删的怪兽数量,p表示“死亡名单”中怪兽的数量
#include<iostream>
#include<cstdio>
using namespace std;
int n,r,b,a[100005],p;
int main(){//freopen("duel.in","r",stdin);//freopen("duel.out","w",stdout);cin>>n;for(int i=1;i<=n;i++){cin>>r;a[r]++;}for(int i=1;i<=100000;i++){if(a[i]){b+=min(p,a[i]);p-=min(p,a[i]);p+=a[i];}}cout<<n-b;//fclose(stdin);//fclose(stdout);return 0;
}
T2 超速检测(detect)
赛时炸了20pts,大悲
P11232超速检测
题意
一条主干道,汽车从 d i d_i di进入,初速度为 v i v_i vi,加速度为 a i a_i ai,一些位置上有超速检测仪,速度超过 V V V即为超速,问几辆车会被判定为超速,关最多多少检测仪仍能使这些车被判定为超速
思路
二分
用题目备注好的物理公式 算出汽车超速的时间范围,大致来说,要按照 a i a_i ai与 0 0 0的大小关系分类讨论,不妨设计算出的超速临界点在 x x x处(主干道长为 L L L)
那么a>0时,超速的路段为 [ x , L ] [x,L] [x,L];a<0时,超速的路段为 [ d i , x ] [d_i,x] [di,x];a=0时没有临界点这一说法,要么全超速,要么全部不超。
可问题在于题目问的不是哪些路段超速,而是在哪些检测仪旁边超速
所以说用二分就好了,upper_bound和lower_bound应该是能够解决的
当然,如果你和我一样,根本就不太记得这两个是怎么用的话,你也可以手写一个。
那么我们现在大概就知道每一辆车是在哪些检测仪旁边超速了对吧
我们能够得到 a n s 1 ans_1 ans1个检测区间,而 a n s 1 ans_1 ans1是第1问的答案
那么每个区间内我们要至少保留一个检测仪,那么剩下来就是贪心的问题了。
贪心
各位可以稍稍忘记我们刚刚讲的题目是什么了。
现在的问题是:我们有 m m m个区间 [ l i , r i ] [l_i,r_i] [li,ri],每个区间中至少有一个数被选中,问最少选中几个数?
为了最大化的重复利用被选的数,我们将区间按照右端点从小到大排序
如果这个区间能够和之前的区间共用被选中的数,则跳过;否则贪心的选中最右边的数,方便与后面的区间共用
代码
这个真不是赛时代码了……赛时因为没有判断加速度为正且初速度大于超速速度的情况(即算出的临界点在出发点之前),炸了20pts,也炸了20多名
稍微改了一点点,看起来比较清晰,但也保留了一点赛时代码的凌乱……
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#define ll long long
#define N 1000005
using namespace std;
int T;
ll n,m,l,V,ans;
ll d[N],a[N],v[N],p[N],ans1;
struct node{int b,c;
}k[N];
bool cmp(node x,node y){if(x.c!=y.c) return x.c<y.c;return x.b<y.b;
}
//作用类似于upper和lower_bound作用的手写函数
ll lb(int x){if(p[1]>x) return -1;int l=1,r=m;while(l<r){int mid=(l+r+1)/2;if(p[mid]>x) r=mid-1;else l=mid;}return l;
}
ll ub(int x){if(p[m]<x) return -1;int l=1,r=m;while(l<r){int mid=(l+r)/2;if(p[mid]<x) l=mid+1;else r=mid;}return l;
}
int main(){//freopen("detect.in","r",stdin);//freopen("detect.out","w",stdout);cin>>T;for(int ca=1;ca<=T;ca++,ans1=0){cin>>n>>m>>l>>V;for(int i=1;i<=n;i++) scanf("%lld%lld%lld",&d[i],&v[i],&a[i]);for(int i=1;i<=m;i++) scanf("%lld",&p[i]);for(int i=1;i<=n;i++){if(a[i]==0){if(v[i]>V){ll o=ub(d[i]);if(o!=-1){k[++ans1].b=o,k[ans1].c=m;}}continue;}double x=(1.0*V*V-1.0*v[i]*v[i])/(2.0*a[i])+d[i];if(a[i]>0){ll y=(ll)x+1;//取整if(y<d[i]) y=d[i];//如果临界点小于出发点,则改为出发点if(y>l) continue;if(ub(y)!=-1){ll o=ub(y);k[++ans1].b=o,k[ans1].c=m;}}else{ll y=(ll)x;if(y==x) y--;//取整if(y>l) y=l;//如果临界点大于终点,则改为终点int o=lb(y);if(o!=-1&&(p[o]>=d[i])){//要保证临界检测仪在出发点右边k[++ans1].b=ub(d[i]),k[ans1].c=o;}}}sort(k+1,k+1+ans1,cmp);ll e=0,g=0;for(int i=1;i<=ans1;i++){if(k[i].b>g) e++,g=k[i].c;}cout<<ans1<<" "<<m-e<<endl;}//fclose(stdin);//fclose(stdout);return 0;
}
额,今天是2025.11.12,很遗憾这篇有点烂尾了,哦对不起,好像是没有结尾,但我并不打算写完它了。
