2025重庆市赛补题
第十三届重庆市大学生程序设计竞赛
Problem F. 赢麻了
问题
给三个数a, b, c,若:
1. a > b ,输出 “Win”
2. c > b ,输出 “WIN”
3. 其他情况,输出 “nowin”
注意大小写即可
代码
#include<bits/stdc++.h>
using namespace std;
int T,n;
int a,b,c;
int main()
{
scanf("%d",&T);
while(T--) {
scanf("%d%d%d",&a,&b,&c);
if(a>b) printf("Win\n");
else if(c>b) printf("WIN\n");
else printf("nowin\n");
}
return 0;
}
Problem A. 题目背景是 GPT 给的
问题
给n个数,进行若干次操作,第 i 次操作令\(a_j=a_j|a_{(i+j)\%n}\) ,问最少几次操作使得这n个数全部相等
思路
乍一看模拟是\(O(n^2)\),但仔细分析
当两个数进行或运算时,二进制下,新的数继承了两个数的所有1,因此每次操作发散的速度是很快的,模拟也能过
代码
#include<bits/stdc++.h>
using namespace std;
int T,n;
int a[200100];
int b[200100];
bool check() {
for(int i=2;i<=n;i++) {
if(a[i]!=a[1]) return 1;
}
return 0;
}
int main()
{
scanf("%d",&T);
while(T--) {
scanf("%d",&n);
for(int i=1;i<=n;i++) {
scanf("%d",&a[i]);
}
int ans=1;
for(ans;check();ans++) {
for(int j=1;j<=n;j++) b[j]=a[j];
for(int j=1;j<=n;j++) {
a[j]=(b[j] | b[(j+ans-1)%n+1]);
//printf("%d %d %d %d\n",b[j],b[(j+ans-1)%n+1],(b[j] | b[(j+ans-1)%n+1]),a[j]);
}
//for(int j=1;j<=n;j++) printf("%d ",a[j]);
//printf("\n");
}
printf("%d\n",ans-1);
}
return 0;
}
Problem H. 连接
问题
有2n个球,其中n个球紧贴下边界,标号为1-n(无序),还有n个球分布在上边界,标号1-n(无序且不一定紧贴上边界),现对标号相同的球进行连线,问是否会出现交叉的情况

思路
既然下边界都是紧贴的,那我们的思考重心应该放在上边界上。
一开始的想法是从两边往中间找,不难发现分布在左右最外面的如果是不紧贴的,则可以绕过去并转换为最外面是紧贴的的情况;再看如果最外面的是紧贴的,又好像找不出什么性质,不能和内部紧贴的逆序,而和内部不紧贴的逆序与否都无所谓----进一步思考内部不紧贴的和内部紧贴的球之间的关系,发现逆序与否也都不影响,故得到一项关键性质:不紧贴的球可以直接忽略
那剩下的球中,唯一的要求便是不能逆序,否则便会交叉,故找出上下紧贴的球中是否存在逆序即可
代码
#include<bits/stdc++.h>
using namespace std;
int T,n;
int a[200100];
int b[200100];
int c[200100];
map<int,int>Map;
int main()
{
scanf("%d",&T);
while(T--) {
scanf("%d",&n);
for(int i=1;i<=n;i++) {
scanf("%d",&a[i]);
}
for(int i=1;i<=n;i++) {
scanf("%d",&b[i]);
}
for(int i=1;i<=n;i++) {
scanf("%d",&c[i]);
if(c[i]==0) Map[a[i]]=1;
}
/*for(int i=1;i<=n;i++) {
if(c[i]==0) {
Map[b[i]]=1;
}
}*/
int cnt=0,flag=0;
for(int i=1;i<=n;i++) {
if(c[i]==0) continue;
cnt++;
while(Map.find(b[cnt])!=Map.end()) cnt++;
if(a[i]!=b[cnt]) {
flag=1;
break;
}
}
if(flag) printf("No\n");
else printf("Yes\n");
Map.clear();
}
return 0;
}
Problem C. 区域划分
问题
将一个n*n的正方形格子分成n个区块并进行涂色,要求:
• 相同区域的格子的颜色必须相同。
• 相邻区域的格子颜色必须不同。(区域 i 与区域 j 相邻,当且仅当存在一个区域 i 中的格子与一个区域 j 中的格子共享一条边)
求如何划分能使得最少的涂色数最大
思路
最后几十分钟看了一眼这个题,这不一眼四色定理吗直接n>3时输出4种颜色的情况秒了,结果wa,还以为数学不存在了继续死磕L去了
赛后细看,和四色定理的涂色不同的是这道题的相同区块可以不相邻,放地图上就是飞地,所以别脑子一抽题都没读懂就开写
没看懂题解他在说什么,于是考虑让每个区块和尽可能多的方块相邻,这里我就找个规律,发现这么排能让每个区块的每个方块都能和四个不同区块相邻(当然可能会重复),如下,当n=6时:

即1-n按顺序排列,接下来每一行往右移i*(i+1)/2格,这样做剩下两行是多余的,随便排都行
代码
#include<bits/stdc++.h>
using namespace std;
int T,n;
int a[2000][2000];
int b[20][20];
int main()
{
scanf("%d",&T);
while(T--) {
scanf("%d",&n);
for(int i=1;i<=n;i++) {
for(int j=1;j<=n;j++) {
int add=max(i*(i+1)/2-1,0);
a[i][(j+add-1) % n+1]=j;
}
}
for(int i=1;i<=n;i++) {
for(int j=1;j<=n;j++) {
printf("%d ",a[i][j]);
}
printf("\n");
}
}
return 0;
}
Problem L. 栈与重复
问题
给你一个栈 S ,初始为空,接下来有 n 个操作,每个操作是如下操作之一:
• Push x :x为非负整数。
• Pop :将 S 中栈顶元素弹出,题目保证执行该语句时栈 S 始终非空。
• Repeat :重复执行一遍此前的所有语句(不包含本语句)。
思路
一来就看到这道题,第一眼觉得可能是签到,于是开始死磕了(恼)
显然因为repeat的存在,我们不能模拟,但出现repeat我们只用将ans*2即可,push就加,pop就得减栈顶元素。因为pop的存在,我们还需要想办法把栈给存起来,于是想到了一种奇妙的方法:
push和pop都正常进行(在一个vector中),当遇到了repeat,我们就新开一层vector(初始为空),接下来每次push都在最新的一层里push,如果该层pop完了,我们记录一个pop值,代表在这一层已经往前pop了cnt_i 个,然后往上一层递归进行查找要pop的数(但不进行更改,在最新一层的pop值里记录即可),每一一层都代表了一次repeat。但实现起来太过臃肿,dfs写的依托,码力有所下降(而且为了应对repeat过多的情况还得进行删层的优化,更繁琐了)
看完题解感觉豁然开朗,当栈内元素大于n时忽略repeat即可,居然如此简洁
代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
int n;
char s[200];
deque<int>a;
int MOD=998244353;
signed main()
{
int x=0;
int sum=0;
int flag=0;
scanf("%lld",&n);
for(int i=1;i<=n;i++) {
scanf("%s",&s);
if(s[1]=='u') {//Push
scanf("%lld",&x);
a.push_back(x);
sum+=x;
sum%=MOD;
//if(a.size()>n)a.pop_front();
} else if(s[1]=='o') {//Pop
sum-=a.back();
if(sum<0)sum+=MOD;
a.pop_back();
} else if(s[1]=='e') {//Repeat
sum*=2;
sum%=MOD;
if(a.size()<n&&!flag) {
int size=a.size();
auto it=a.begin();
for(int j=1;j<=size;j++,it++) {
a.push_back(*it);
}
} else {
flag=1;
}
}
/*for(auto var : a) {
printf("%lld ",var);
}
printf("\n");*/
printf("%lld\n",sum);
}
return 0;
}
/*
999
push 1
push 2
re
re
pop
pop
*/
Problem B. 列车
问题
有n个站,m辆列车,给出每辆列车的起点l、终点r、最大容量c,每辆车只能在起点上车,但可以在l-r任意一站下车,1站台有无限个人,问最多能载多少人到n站台
思路
看题解说是最大流最小割,于是粗略学习了下网络流,的确很像,重点在于怎么建图。
从 l -> r 建立一条边,然后若 l, r 中间有新的 l' 则插入并变成下面的形状:l -> l' -> r ->r' 或 l -> l' -> r' -> r(不同的情况取决于r'的大小)
不妨取第一种,则从左往右每条边的权重变为: c, c'+c ,c', 建完图找最大流即可
然后发现这样建图最后会变成一条线,诶这一个个的插入不变成链表了吗,再看权值的计算诶这不变成区间加法了吗,诶这tm线段树或者差分秒了啊
不过注意n的取值范围为1e9,所以要离散化一下,把每个区间加完后找最小割(这里就最小的一条边)即可
在看题解之前还想到了一种贪心的方法,对每辆车按起点进行排序,然后枚举每辆车:
如果其左端点为1,则按 r 升序存进一个map里(map值为其容量,这里map等效于优先队列)。
否则,将其 r 与map里的r进行比较:
若 r 大于 map_begin的r,代表新列车的终点更远,比map_begin更优,于是尽可能抢map_begin的乘客,若抢完了还没满,则重复上一层判定
若 r 等于 map_begin的r,代表终点相同,将map_begin的 c 加上 train[i] 的 c ,进入下一个i
否则,将train[i]插进map里,进入下一个i
但是WA on test 3,再找找是bug还是贪心是错的
代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
int T,n,m;
struct node{
int l;
int r;
int c;
int people;
}a[200100];
int comp(node X,node Y) {
if(X.l==Y.l) return X.r<Y.r;
return X.l<Y.l;
}
map<int,int>f;
int pre[600100];
signed main()
{
scanf("%lld",&T);
while(T--) {
scanf("%lld%lld",&n,&m);
for(int i=1;i<=m;i++) {
scanf("%lld%lld%lld",&a[i].l,&a[i].r,&a[i].c);
f.insert({a[i].l,1});
f.insert({a[i].r,1});
//f.insert({a[i].r+1,1});
}
sort(a+1,a+m+1,comp);
int L=a[1].l,R=a[1].r;
for(int i=2;i<=m;i++) {
if(a[i].l<=R) {
R=max(R,a[i].r);
}
}
if(R!=n||L!=1) {
printf("0\n");
f.clear();
continue;
}
int cnt=1;
for(auto &it : f) {
it.second=cnt;
cnt++;
}
for(int i=1;i<=m;i++) {
pre[f[a[i].l]]+=a[i].c;
pre[f[a[i].r]]-=a[i].c;
//printf("%lld %lld\n",pre[f[a[i].l]],pre[f[a[i].r]+1]);
}
int ans=1e18,res=0;
for(int i=1;i<=f.size()-1;i++) {
//printf("i=%lld %lld\n",i,pre[i]);
res=res+pre[i];
ans=min(ans,res);
}
printf("%lld\n",ans);
for(int i=0;i<=m*3+10;i++) {
pre[i]=0;
}
f.clear();
}
return 0;
}
/*
#include<bits/stdc++.h>
#define int long long
using namespace std;
int T,n,m;
struct node{
int l;
int r;
int c;
int people;
}train[200100];
map<int,int>Map;
int comp(node X,node Y) {
return X.l<Y.l;
}
signed main()
{
scanf("%lld",&T);
while(T--) {
scanf("%lld%lld",&n,&m);
for(int i=1;i<=m;i++) {
scanf("%lld%lld%lld",&train[i].l,&train[i].r,&train[i].c);
}
sort(train+1,train+1+m,comp);
for(int i=1;i<=m;i++) {
if(train[i].l==1) {
train[i].people=train[i].c;
Map[train[i].r]+=train[i].c;
} else {
while(!Map.empty()) {
auto it = Map.begin();
if((*it).first<train[i].l) {
Map.erase(it);
continue;
}
if(train[i].r==(*it).first) {
(*it).second+=train[i].people;
break;
} else if(train[i].r>(*it).first) {//如果r_i大于r_map,乘客转移
if(train[i].c-train[i].people>=(*it).second) {
train[i].people+=(*it).second;
Map.erase(it);
} else {
(*it).second-=train[i].c-train[i].people;
train[i].people=train[i].c;
Map[train[i].r]+=train[i].people;
break;
}
} else {
if(train[i].people) Map[train[i].r]+=train[i].people;
break;
}
}
}
}
if(Map.find(n)!=Map.end()) printf("%lld\n",Map[n]);
else printf("%lld\n",0);
Map.clear();
}
return 0;
}*/
Problem J. RGB 树
前面的区域等下周再探索吧~

浙公网安备 33010602011771号