accuber

导航

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] 逆序对数列 - 洛谷

待补。。。

posted on 2026-01-16 22:39  accuber  阅读(0)  评论(0)    收藏  举报