2026团体程序设计天梯赛L1+L2题解

大三老登第三年参加天梯赛,今年的题目真的简单好多。下面是本人在赛场上的代码合集:

L1 – 1

print("Building the Future, One Line of Code at a Time.")

最简单的入门题目,使用Py更快的解决。

L1 – 2

题意为每年的天梯赛有 15 道题目告诉你比赛的年数让你算题目总数,直接输出

15×n15 \times n

即可。

#include<bits/stdc++.h>
using namespace std;

int main(){
	int n; cin >> n;
	cout << n * 15;
	return 0;
}

L1 – 3

题目给出两个年份A和B,B – A 代表年龄让你判断:

  • 如果该寿命超过了人类最长寿命,在第二行中输出 jiu ting tu ran de...
  • 如果该寿命不是正数,输出 hai sheng ma?
  • 如果寿命在正常范围内,输出 nin tai cong ming le!

简单分支结构判断一下即可。

#include<bits/stdc++.h>
using namespace std;

int main(){
	int a, b; cin >> a >> b;
	cout << b - a << endl;
	if(b - a <= 0) cout << "hai sheng ma?";
	else if(b - a > 250) cout << "jiu ting tu ran de...";
	else cout << "nin tai cong ming le!";
	return 0;
}

L1 – 4

给出所有参赛学生所属高校的评级分,统计高校评级分数小于1700的学生总人数。

简单循环加判断即可。

#include<bits/stdc++.h>
using namespace std;

int main(){
	int n; cin >> n;
	int cnt = 0;
	while(n --){
		int t; cin >> t;
		if(t < 1700) cnt ++;
	}
	cout << cnt;
	return 0;
}

L1 – 5

给出多个序号和是否被骂,寻找这些序号里仅仅被骂的人的数量。

使用map来存储状态,因为map是有序的可以顺便解决顺序输出的问题。

当且仅当map键值对中的值为0的时候更新(如果状态为0则不变,状态为1可以被记录),最后只需要找到状态为0的键即可。

#include<bits/stdc++.h>
using namespace std;

int main(){
	int n; cin >> n;
	map<int,bool> mp;
	for(int i = 0;i < n;i ++){
		int id, f; cin >> id >> f;
		if(mp[id] == 0) mp[id] = f;
	}
	int f = 0;
	for(auto [k,v] : mp)
		if(v == 0){
			if(f == 0) cout << k, f = 1;
			else cout << ' ' << k;
		} 
	if(f == 0) cout << "NONE";
	return 0;
}

L1 – 6

给出11行字符串,以m的数量来表达数字0-9。

使用getline和string直接秒。

#include<bits/stdc++.h>
using namespace std;

int main(){
	for(int i =0 ;i < 11;i ++){
		string s; getline(cin,s);
		cout << s.size();
	}
	return 0;
}

L1 – 7

给出n个值,找出最大值、最小值、平均值,并且标出超过平均值2倍的值对应的下标。

最大最小值直接调用函数或者写个循环,然后求和算平均值。算完后再次遍历数组即可。

#include<bits/stdc++.h>
using namespace std;

int main(){
	int n; cin >> n;
	vector<int> a(n);
	for(int i = 0;i < n;i ++) cin >> a[i];
	cout << *max_element(a.begin(),a.end()) << ' ' << *min_element(a.begin(),a.end()) << ' ';
	int avg = 0;
	for(int i = 0;i < n;i ++) avg += a[i];
	avg /= n; cout << avg << endl;
	int f = 0;
	for(int i = 0;i < n;i ++)
		if(a[i] > 2 * avg){
			if(f == 0) cout << i + 1, f = 1;
			else cout << ' ' << i + 1;
		}
	if(f == 0) cout << "Normal";
	return 0;
}

L1 – 8

给定一个字符串,对其进行如下操作:

  1. 查找指定字符串 s1​ 前 13 次出现的位置;
  2. 在指定位置 p 插入一个指定字符串 s2​的第 1 个字符;
  3. 将某一段连续的字符串翻转。

对于第一种操作可以使用类似于2024年猫娘题目的写法,使用find函数后将第一个字符直接替换成#然后进行下一次find即可。

对于第二种操作直接使用string的insert函数即可。

对于第三种操作直接使用reverse函数即可。

#include<bits/stdc++.h>
using namespace std;
string s;

void ch1(){
	string mod, t = s; cin >> mod;
	for(int i = 0;i < 3;i ++){
		int idx = t.find(mod);
		if(i == 0 && idx != -1){
			cout << idx;
			t[idx] = '#';
		}
		else if(i == 0 && idx == -1){
			cout << -1; break;
		}
		else if(i != 0 && idx != -1) {
			cout << ' ' << idx;
			t[idx] = '#';
		}
		else if(i != 0 && idx == -1) break;
	}
	cout << endl;
}

void ch2(){
	string mod;
	int idx;
	cin >> idx >> mod;
	s.insert(idx,mod);
	cout << s << endl;
}

void ch3(){
	int l, r; cin >> l >> r;
	reverse(s.begin() + l, s.begin() + r + 1);
	cout << s << endl;
}

int main(){
	int n; cin >> n >> s;
	while(n --){
		int op; cin >> op;
		if(op == 1) ch1();
		else if(op == 2) ch2();
		else ch3();
	}
	return 0;
}

L2 – 1

给定一个数组和阈值T如果数组的值小于等于T则输出其在原数组中对应的下标序号,将未输出下标的数组成一个新的数组,计算新的T重复执行这个操作直到新数组为空。

简单使用模拟即可,开辟一个数组zu来存放未输出的数和其对应的下标序号,注意顺序是逆序的。然后写个死循环直到zu数组生成为空结束即可。

#include<bits/stdc++.h>
using namespace std;

int main(){
	int n, t; cin >> n >> t;
	vector<int> a(n), ans;
	for(int i = 0;i < n;i ++) cin >> a[i];
	vector<pair<int,int>> zu;
	for(int i = 0;i < n;i ++)
		if(a[i] <= t) ans.push_back(i + 1);
		else zu.push_back({a[i],i + 1});
	reverse(zu.begin(),zu.end());
	while(zu.size()){
		int newt = 0;
		vector<pair<int,int>> t;
		for(int i = 0;i < zu.size();i ++) newt += zu[i].first;
		newt /= (int)zu.size();
		for(int i = 0;i < zu.size();i ++)
			if(zu[i].first <= newt) ans.push_back(zu[i].second);
			else t.push_back(zu[i]);
		reverse(t.begin(),t.end());
		zu = t;
	}
	for(int i = 0;i < ans.size();i ++)
		if(i == 0) cout << ans[i];
		else cout << ' ' << ans[i];
	return 0;
}

L2 – 2

给定一个数组,输出其最大数在数组中的下标序号。并进行m次查询,寻找每次大于x的第一个数在数组的下标,如果不存在输出0。

需求1可以直接计算最大值然后遍历输出即可,需求2可以将数组排序随后使用upper_bound二分寻找大于x的第一个数,随后判断一下是否存在即可。使用map来记录每个数第一次出现的下标序号即可。

#include<bits/stdc++.h>
using namespace std;

int main(){
	int n; cin >> n;
	vector<int> a(n);
	map<int,int> mp;
	for(int i = 0;i < n;i ++){
		cin >> a[i];
		if(mp[a[i]] == 0) mp[a[i]] = i + 1;
	}
	int mx = *max_element(a.begin(),a.end()), f = 0;
	for(int i = 0;i < a.size();i ++)
		if(a[i] == mx)
			if(f == 0) cout << i + 1, f = 1;
			else cout << ' ' << i + 1;
	cout << endl;
	sort(a.begin(),a.end());
	int m; cin >> m;
	while(m --){
		int x; cin >> x;
		int idx = upper_bound(a.begin(),a.end(),x) - a.begin();
		if(idx == n) cout << 0 << endl;
		else cout << mp[a[idx]] << endl;
	}
	return 0;
}

L2 – 3

给定一棵树,求从跟节点开始到达所有叶子节点的路径中权值的最小值最大为多少并输出该路径的叶子节点序号(从小到大)。

直接使用dfs遍历即可,到达根节点时更新答案和叶子节点数组即可。

#include<bits/stdc++.h>
using namespace std;
int n, mxans = -1;
vector<int> ans;
vector<pair<int,int>> g[100010];
void dfs(int u,int mx){
	if(g[u].empty()){
		if(mxans < mx){
			mxans = mx;
			ans.clear();
			ans.push_back(u);
		}
		else if(mxans == mx) ans.push_back(u);
	}
	for(auto [v,val] : g[u])
		dfs(v,min(mx,val));
}

int main(){
	cin >> n;
	for(int u = 1;u < n;u ++){
		int v, val; cin >> v >> val;
		g[v].push_back({u, val});
	}
	dfs(0,1e9);
	cout << mxans << endl;
	sort(ans.begin(),ans.end());
	for(int i = 0;i < ans.size();i ++)
		if(i == 0) cout << ans[i];
		else cout << ' ' << ans[i];
	return 0;
}

L2 – 4

对每个给定起点,在有向图中每次贪心选择“概率最大、若并列则编号最小且未访问过”的下一节点,不断前进直到无路可走,并输出整条路径。

简单来说对每一个节点的子节点按概率排序,概率相同的序号小的优先。

使用广度优先遍历从每次询问的起始节点开始,每次找到未在该链中出现过的最大概率的节点直到没有。

#include<bits/stdc++.h>
using namespace std;
int n, m;
vector<pair<int,int>> g[100010];

bool cmp(pair<int,int> a, pair<int,int> b){
	return a.second > b.second || (a.second == b.second) && (a.first < b.first);
}

int main(){
	cin >> n >> m;
	for(int i = 0;i < m;i ++){
		int id1, id2, p; cin >> id1 >> id2 >> p;
		g[id1].push_back({id2,p});
	}
	for(int i = 1;i <= n;i ++) sort(g[i].begin(),g[i].end(),cmp);
	int k; cin >> k;
	while(k --){
		int q; cin >> q;
		bool mp[10001] = {false};
		queue<int> qu;
		qu.push(q);
		mp[q] = true;
		cout << q;
		while(!qu.empty()){
			int u = qu.front();
			qu.pop();
			for(int i = 0;i < g[u].size();i ++)
				if(mp[g[u][i].first] == false){
					cout << "->" << g[u][i].first;
					mp[g[u][i].first] = true;
					qu.push(g[u][i].first);
					break;
				}
		}
		cout << endl;
	}
	return 0;
}

此处mp不能使用map否则会超时,该做法不是最优解法但是胜在好写。

如何评价2026年第十一届CCCC团体程序设计天梯赛?

来自线下赛点志愿者负责人的回复

恍如隔世今年已经是第三年负责团体程序设计天梯赛的志愿者工作,从大一新登已经变成大三老登了。并且已经退役很久了,我的算法竞赛生涯自天梯赛开始也许也会由天梯赛而终。曾记得,大一时因为天梯赛选拔进入校队。不久后因为想逃课所以去帮助指导老师布置竞赛的机房,也是从那时起我被选为了下一任负责人并一直负责至今。虽然我已不再参加大部分的社团活动,但是对于算法集训队我一直从大一负责至今。这一切可能来源于对算法竞赛的热爱,至今我依旧在做着队员培训、比赛报名、财务报销等职务,为我校的集训队发光发热。我的算竞梦,来自于天梯赛。

我校是多年的天梯赛线下赛点我们也一直致力于让选手有更好的参赛体验。自去年起,团体程序设计天梯赛支持了更多的编译器。我校提供了小熊猫c++,今年我校开始使用Idea、Clion、Pycharm、Vscode等更新的编程IDE,来确保选手们能有与自己平时训练相近的环境来进行比赛。比较可惜的是今年的餐食不再是麦当劳,而是换成了KFC(汉堡或者轻食的套餐)。

来自加班排座位的牛马,希望明天来赛点参赛的各位都能有好成绩!

转自本人在(https://www.zhihu.com/question/2026312271513630465/answer/2028618712303826217)的回复

天梯赛2025 L2-054 三点共线 题解

题意简述

输入 n 个坐标其中 y 轴只能出现 {0,1,2} 三个值,x 轴数据范围为 [-1e6,1e6]。

你需要在这些点中寻找三点共线的点并按要求输出。

题解思路

其实我们可以通过数学推导可知,只需要计算斜率相同即可。可推出公式如下:

y1y0=y2y1y_1 – y_0 = y_2 – y_1
y2=2×y1y0y_2 = 2 \times y_1 – y_0

我们可以思考其实只需要枚举 y1 和 y0 随后查找 y2 是否存在就行。

#include<bits/stdc++.h>
using namespace std;

const int N = 1e6 + 1000;
int y2[2 * N] = {0};

int main(){
	int n; cin >> n;
	vector<int> y0, y1;
	for(int i = 0;i < n;i ++){
		int x, y; cin >> x >> y;
		if(y == 0) y0.push_back(x);
		else if(y == 1) y1.push_back(x);
		else y2[x + N] = 1;
	}
	sort(y0.begin(),y0.end());
	y0.erase(unique(y0.begin(),y0.end()),y0.end());
	sort(y1.begin(),y1.end());
	y1.erase(unique(y1.begin(),y1.end()),y1.end());
	int f = -1;
	for(int j = 0;j < y1.size();j ++)
		for(int i = 0;i < y0.size();i ++){
            int num = 2 * y1[j] - y0[i];
			if(abs(num) <= 1e6 && y2[2 * y1[j] - y0[i] + N] == 1){
				cout << '[' << y0[i] << ", 0] " << '[' << y1[j] << ", 1] " << '[' << (2 * y1[j] - y0[i]) << ", 2]" << endl;
				f = 1;
			} 
		}
	if(f != 1) cout << f;
	return 0;
}

按照题目要求我们需要对原坐标点做排序去重处理,而且因为需要 y1 优先,所以外层循环必须为 y1 的循环。

我们在查找 y2 的时候无法使用哈希表,因为查找时间并不是完全的 O(1)。

2026年如何快速部署Domjudge9.0.0

操作系统推荐

Ubuntu 22.04
Ubuntu 24.04
debain 13
debain 12

相关依赖

sudo apt update
sudo apt upgrade -y
sudo apt install acl zip unzip mariadb-server nginx php-fpm php-gd php-cli php-intl php-mbstring php-mysql php-curl php-json php-xml php-zip composer ntp make gcc g++ debootstrap pkg-config libcgroup-dev lsof procps libcurl4-gnutls-dev libjsoncpp-dev libmagic-dev -y

下载官方压缩包

cd /opt/
sudo wget https://www.domjudge.org/releases/domjudge-9.0.0.tar.gz
sudo tar -zxvf domjudge-9.0.0.tar.gz
rm domjudge-9.0.0.tar.gz

编译 Domjudge

cd /opt/domjudge-9.0.0
sudo ./configure --prefix=/opt/domjudge --with-domjudge-user=root --with-baseurl=127.0.0.1

结束无报错后,运行下方命令。中途有询问输入yes即可。

sudo make domserver

安装 Domjudge 服务端

安装domserver、初始化并安装数据库结构及配置前端。

sudo make install-domserver
cd /opt/domjudge/domserver
sudo bin/dj_setup_database -s install
sudo ln -s /opt/domjudge/domserver/etc/nginx-conf /etc/nginx/sites-enabled/domjudge
sudo ln -s /opt/domjudge/domserver/etc/domjudge-fpm.conf /etc/php/8.3/fpm/pool.d/domjudge.conf
sudo service php8.3-fpm reload
systemctl daemon-reload

配置nginx

cd /etc/nginx/sites-enabled
sudo rm default
sudo service nginx reload
cd /opt/domjudge/domserver
sudo chown www-data:www-data -R webapp/public/*
cd /opt

随后即可通过:http://你服务器的IP地址/domjudge访问主页。
默认账号admin密码可通过下面命令获取:

sudo cat /opt/domjudge/domserver/etc/initial_admin_password.secret

配置 php 和 mysql 使得服务正常运行

配置 PHP

vim /etc/php/8.3/fpm/pool.d/domjudge.conf

找到php_admin_value[memory_limit]改为php_admin_value[memory_limit] = 1024Mphp_admin_value[date.timezone]改为php_admin_value[date.timezone] = Asia/Shanghai

接下来保存退出,并应用。

sudo service php8.3-fpm reload
sudo systemctl restart php8.3-fpm.service

配置 mysql

vim /etc/mysql/conf.d/mysql.cnf

将内容的相应部分改成下方模样:

[mysqld]
max_connections = 1000
max_allowed_packet = 1024MB
innodb_log_file_size = 5120MB

继续配置

vim /etc/mysql/mariadb.conf.d/50-server.cnf

max_allowed_packet = 1G 取消注释。

保存并重启数据库:

sudo systemctl restart mysql

如果一切正常,刷新 DOMjudge 的 web 页面,点进 config checker,你会看到全绿。

配置 judgehost 评测机

本文采用 docker 部署。

安装 docker

此处使用了LinuxMirrors的快速部署脚本。

bash <(curl -sSL https://linuxmirrors.cn/docker.sh)

按照提示选择即可。

配置 cgroups

sudo vim /etc/default/grub

GRUB_CMDLINE_LINUX_DEFAULT添加下面的配置:

GRUB_CMDLINE_LINUX_DEFAULT="quiet cgroup_enable=memory swapaccount=1 systemd.unified_cgroup_hierarchy=0"

保存,随后确保你的服务器没有正在运行的其他服务,保存并重启服务器。

sudo update-grub
sudo reboot

安装评测机

获取 <domserver password>

sudo cat /opt/domjudge/domserver/etc/restapi.secret

随后安装评测机,将下面命令里的部分补充完整:

sudo docker run -d -it --restart=always --privileged -v /sys/fs/cgroup:/sys/fs/cgroup --name judgehost-1 --hostname judge1 --network="host" -e DAEMON_ID=1 -e CONTAINER_TIMEZONE=Asia/Shanghai -e JUDGEDAEMON_PASSWORD=<domserver password> -e DOMSERVER_BASEURL=http://你的Domjudge服务端IP/domjudge/ domjudge/judgehost:9.0.0

如需部署多台请更改--name--hostnameDAEMON_ID=后面的值。
随后可以前往 Domjudge 管理页面的 judgehousts 查看是否上线。