2026年嘉应学院寒假算法冬令营结训赛
A B2029 大象喝水 - 洛谷
数学题,对于圆周率 π ,可以×100之后再除回去,避免小数带来的精度误差。
void solve(){
int h,r;
cin>>h>>r;
int sum=h*314*r*r;
//cout<<sum<<endl;
int res=20000*100;
if(res%sum)cout<<res/sum+1<<endl;
else cout<<res/sum<<endl;
}
B P1152 欢乐的跳 - 洛谷
模拟题,把两相邻数之差用st数组标记即可。注意相差过大可能会造成越界问题,需要加个判断。
void solve(){
cin>>n;
for(int i=1;i<=n;i++)cin>>a[i];
for(int i=2;i<=n;i++){
int tmp=abs(a[i]-a[i-1]);
if(tmp<n)st[tmp]=1;
}
int flag=1;
for(int i=1;i<=n-1;i++){
if(st[i]==0){
cout<<"Not jolly"<<endl;
return;
}
}
cout<<"Jolly"<<endl;
}
C P1923 【深基9.例4】求第 k 小的数 - 洛谷
题目标签所提到了分治法, 可以理解为是我们ACWing快速排序的递归分治的策略,时间复杂度O(n logn)
或者洛谷题解中的快速选择算法:
递归查找,先选择一个基数(可以选择最右侧元素/中位数/随机选择),然后反复进行分区,将数组分为小于基准和大于基准两部分。接下来进入递归处理部分:根据 k 和基数的位置关系,决定选左半边还是右半边。如果 k 就是基数呢?直接输出就好了。 O(n)
如果使用sort,因为数据量过大(超过\(10^5\)), 必须加上加速(关闭流同步)
void solve(){
cin>>n>>k;
for(int i=0;i<n;i++)cin>>a[i];
sort(a,a+n);
cout<<a[k]<<endl;
}
signed main(){
ios::sync_with_stdio(0);
cin.tie(0),cout.tie(0);
int _=1;
//cin>>_;
while(_--)solve();
return 0;
}
D P1216 [IOI 1994 / USACO1.5] 数字三角形 Number Triangles - 洛谷
非常经典的一道 线性DP 的题目。
需要从倒数第二层开始,选择它下面两个位置的值。这个值记作从最后一层开始,路径上的最大总和。具体方法可以参考AcWing 898. 数字三角形 - AcWing
void solve(){
cin>>n;
for(int i=1;i<=n;i++){
for(int j=1;j<=i;j++)
cin>>g[i][j];
}
for(int i=n-1;i>=1;i--){
for(int j=1;j<=i;j++)
g[i][j]+=max(g[i+1][j],g[i+1][j+1]);
}
cout<<g[1][1]<<endl;
}
E P3799 小 Y 拼木棒 - 洛谷
4根木棒组成一个正三角形,则必有 2根长度相等。
且另外2根长度之和,等于前2根相等的木棒 的长度。
意识到这个就显而易见了(虽然我赛时没想出来T-T)
各木棍长度 \(a_i\)≤5000,我们可以预处理每个长度的选择\(C^{2}_{(cnt_{a_i})}\) ,
再从1到\(a_i\)/2 枚举另外两根长度, 时间复杂度 O(\(n^2\)) 。
void solve(){
cin>>n;
for(int i=1;i<=n;i++)cin>>a[i],cnt[a[i]]++;
int res=0;
for(int i=1;i<=5000;i++){
int tmp=cnt[i]*(cnt[i]-1)/2%mod;
if(tmp==0)continue;
//取模要充分,避免超出范围
//如果有负数出现,要(sum%mod+mod)%mod
for(int j=1;j<i/2;j++){
res+=cnt[j]*cnt[i-j]%mod*tmp%mod;
res%=mod;
}
//如果另外两根相等:
if(i%2==0){
res+=cnt[i/2]*(cnt[i/2]-1)/2%mod*tmp%mod;
res%=mod;
}else {
//别漏了这个,是上面j<i/2的遗漏问题T-T
res+=cnt[i/2]*cnt[i/2+1]%mod*tmp%mod;
res%=mod;
}
}
cout<<res<<endl;
}
F P1928 外星密码 - 洛谷
这题是一个比较难的字符串模拟, 赛时没有想到怎么找最右边那个]对应的[ ,
其实向前一个个判断就可以了,不会超时。。。
tip: 主要要找到最里面的那对[],可以用string 的find方法进行查找,
比如 ss.find("]") , 也可以指定从哪个下标开始查找,ss.find("]",idx) ,不指定默认为0
时间复杂度是O (n * m) ,n为主串长度,m为待查找的子串长度
void solve(){
cin>>ss;
int r=ss.find(']');
//循环直到不存在]
while(r!=-1){
int l=r;
//找到离他最近的[
while(ss[l]!='[')l--;
//提取这一对[]中所有内容,第一个参数为从哪里提取,第二个参数为提取的串长度
string tmp=ss.substr(l+1,r-l-1);
string ins="";
//cout<<tmp;
//处理数字
int cnt=0;
if(tmp[0]>='0'&&tmp[0]<='9')cnt=cnt*10+tmp[0]-'0';
tmp.erase(tmp.begin(),tmp.begin()+1);
if(tmp.size()&&tmp[0]>='0'&&tmp[0]<='9'){
cnt=cnt*10+tmp[0]-'0';
tmp.erase(tmp.begin(),tmp.begin()+1);
}
//cout<<cnt<<endl;
//cout<<tmp;
while(cnt--){
ins+=tmp;
}
ss.erase(l,r-l+1);
// cout<<ss<<endl;
//将处理后的结果插回到主串中,第一个参数为插入到哪里,第二个参数为插入的字符串
ss.insert(l,ins);
//cout<<ss;
// break;
r=ss.find(']');
}
cout<<ss;
//
}
G P1591 阶乘数码 - 洛谷
高精度,注意idx控制最高位
void solve(){
cin>>n>>k;
int idx=1;
num[1]=1;
int tmp=0;
for(int i=2;i<=n;i++){
for(int j=1;j<=idx||tmp;j++){
//这里是一大错点,超过上一次结果范围的地方不要×i
if(j<=idx)tmp+=num[j]*i;
num[j]=tmp%10;
tmp/=10;
idx=max(j,idx);
}
}
int res=0;
for(int i=idx;i>=1;i--)
if(num[i]==k)res++;
cout<<res<<endl;
}
H P2234 [HNOI2002] 营业额统计 - 洛谷
震惊,逆天测试样例,暴力就能过了。。。
void solve(){
cin>>n;
int res=0;
for(int i=1;i<=n;i++){
cin>>a[i];
}
res+=a[1];
for(int i=2;i<=n;i++){
int minv=1e18;
for(int j=1;j<i;j++){
minv=min(minv,abs(a[i]-a[j]));
}
res+=minv;
}
cout<<res<<endl;
}
赛后怎么能局限于过题呢?
不能用O(\(n^2\)) , 那正解是什么?
数据结构 set 有一个很好用的k=st.lower_bound(x), 可以在O(n \(log_2 n\))的时间找到set中大于等于x的第一个数,也可以用这个得到小于x的第一个数(k--)
注意lower_bound返回的是一个地址,使用的时候要 * k
void solve(){
cin>>n;
set<int>st;
st.insert(-1e18),st.insert(1e18);
int res=0;
for(int i=1;i<=n;i++){
cin>>a[i];
if(i==1){
res+=a[i];
}else {
auto k= st.lower_bound(a[i]);
int minv=abs(*k-a[i]);
k--;
if(*k!=-1e18){
minv=min(minv,abs(*k-a[i]));
}
res+=minv;
}
st.insert(a[i]);
}
cout<<res<<endl;
}
I P4447 [AHOI2018初中组] 分组 - 洛谷
赛时没想到怎么写,索性跳过了,暂时想不到的题目可以跳过,等其他题目过了再来看(怎么我越跳越多了💦)
一开始看到想二分答案,但是没想到怎么写check
赛后看到dalao的二分查找, 其实我们可以通过维护两个数组,一个数组用于记录一个小队的长度,另一个用来记录该小队最后一个人期望添加的队友能力值。
Q:怎么知道需要插在哪个小队里还是单开一个呢?多组小队所需能力相同时,怎么确保添加到人数最少的那组呢?
A : 排序,同时排序可以发现,相同值时下标越大,该组的大小越小。
int a[N];
int idx;
int need[N];
int siz[N];
void solve(){
cin>>n;
for(int i=1;i<=n;i++)cin>>a[i];
sort(a+1,a+1+n);
need[1]=a[1]+1,siz[1]=1;
idx=2;
for(int i=2;i<=n;i++){
int l=1,r=idx-1;
while(l<r){
int mid=(l+r+1)>>1;
if(need[mid]<=a[i])l=mid;
else r=mid-1;
}
if(need[l]!=a[i]){
need[idx]=a[i]+1,siz[idx]=1;
idx++;
}else {
need[l]++,siz[l]++;
}
}
int minv=1e18;
for(int i=1;i<idx;i++)minv=min(minv,siz[i]);
cout<<minv<<endl;
}
J P3853 [TJOI2007] 路标设置 - 洛谷
二分答案,首先找到单调性:空旷指数越大,所用路标肯定越少
对于两个相邻的路标,如果他们的空旷值小于或等于我们二分出来的x,
那么无需操作;若大于我们的x , 我们就需要使用路标。
需使用个数为$$ \left \lceil \frac{ (a[i]-a[i-1])}{x} \right \rceil -1$$
bool check(int x){
int res=0;
for(int i=1;i<=n+1;i++){
int tmp=a[i]-a[i-1];
if(tmp==0)continue;
int sum=tmp/x;
if(tmp%x)sum++;
res+=sum-1;
if(res>k)return 0;
}
return 1;
}
void solve(){
cin>>l>>n>>k;
for(int i=1;i<=n;i++)cin>>a[i];
a[n+1]=l;
int l=1,r=10000000;
while(l<r){
int mid=l+r>>1;
if(check(mid))r=mid;
else l=mid+1;
}
cout<<l<<endl;
}
K P2719 搞笑世界杯 - 洛谷
赛时觉得我可以直接用组合数写出来, 但是失败了。。。
线性dp
double f[1300][1300];
//设 f[i][j] 为还剩i张A类免费票,j张B类双倍价钱票时,
//最后两个人拿到两张同样的票的几率
void solve(){
cin>>n;
n/=2;
for(int i=2;i<=n;i++)f[i][0]=f[0][i]=1;//如果只剩一种票了,最后两个人拿到两张同样的票的几率就为1
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
//d[i-1][j](当前同学拿了A类票)和d[i][j-1](当前同学拿了B类票)
f[i][j]=f[i][j-1]*0.5+f[i-1][j]*0.5;
}
}
printf("%.4f",f[n][n]);
}
L P1364 医院设置 - 洛谷
链式前向星存图,参考dalao题解:图的遍历:链式前向星详解 - AcWing
之后对每一个点跑一遍bfs就可以啦
#include <bits/stdc++.h>
#define int long long
#define PII pair<int,int>
#define pb push_back
#define fi first
#define se second
const int N=1e6+10,M=1010,mod=1e9+7;
using namespace std;
int n,m,k;
int h[N],e[N],ne[N],w[N],idx;
struct nod{
int id,len;
int from;
};
void add(int a,int b){
e[idx]=b,ne[idx]=h[a],h[a]=idx++;
}
void solve(){
cin>>n;
memset(h,-1,sizeof h);
for(int i=1;i<=n;i++){
int ww,u,v;
cin>>ww>>u>>v;
w[i]=ww;
if(u){
add(i,u),add(u,i);
}
if(v){
add(i,v),add(v,i);
}
}
int minv=1e18;
for(int i=1;i<=n;i++){
int res=0;
queue<nod>qq;
qq.push({i,1,i});
while(qq.size()){
nod tmp = qq.front();qq.pop();
for(int i=h[tmp.id];~i;i=ne[i]){
int j=e[i];
if(j==tmp.from)continue;
res+=tmp.len*w[j];
qq.push({j,tmp.len+1,tmp.id});
if(res>minv)break;
}
}
minv=min(minv,res);
}
cout<<minv<<endl;
}
signed main(){
ios::sync_with_stdio(0);
cin.tie(0),cout.tie(0);
int _=1;
//cin>>_;
while(_--)solve();
return 0;
}
M P4145 上帝造题的七分钟 2 / 花神游历各国 - 洛谷
线段树,也看到有dalao用树状数组写:浅谈树状数组 - AcWing
待补。。。
N P2513 [HAOI2009] 逆序对数列 - 洛谷
待补。。。
浙公网安备 33010602011771号