背景:
最近,我同学参加了这个比赛,个人因为whk不好,没去参加,感觉有点可惜,觉得今年科大的题真是一年难于一年呀!(对于 T3,T4 而言),接下来请让我们来看看这次比赛的题目难度如何吧!
解题:
T1:前置知识:模拟
题目原文:
题目描述
小可可正在研究 W 市的足球联赛。
W 市的足球联赛有 n 支球队,球队编号分别为 1,2,3…n,每个赛季,球队之间会进行比赛。
已知本赛季进行了 m 场比赛,第 i 场是 ai,bi 两支球队进行比赛,并且比分是 ci:di,根据比分判定胜负,从而影响积分。
- 如果 ci>di,则 ai 球队获胜。
- 如果 ci<di,则 bi 球队获胜。
- 如果 ci=di,则 ai 和 bi 球队平局。
赛后积分变化:每场比赛如果分出胜负,则胜者得 3 分,败者不得分;否则平局双方各得 1 分。
需要求出 m 场比赛后每支球队的积分。
思路:模拟以上过程即可,代码仅供参考:
#include<bits/stdc++.h>
using namespace std;
#define int long long
int n, m, a[10001], b[10001], c[10001], d[10001];
map<int, int>mp;
signed main() {
cin >> n >> m;
for (int i = 1; i <= m; i++) cin >> a[i] >> b[i] >> c[i] >> d[i];
for (int i = 1; i <= m; i++) {
if (c[i] > d[i]) mp[a[i]] += 3;
else if (c[i] < d[i]) mp[b[i]] += 3;
else mp[a[i]] += 1, mp[b[i]] += 1;
}
for (int i = 1; i <= n; i++) cout << mp[i] << " ";
return 0;
}
T2:前置知识:贪心
题目原文:
题目描述
小可可买了美味的果汁,但他太懒了,于是要求奶龙给他倒果汁。
有 m 个水杯,第 i 个水杯容量为 Vi。现在有 n 个单位体积的果汁,小可可要求奶龙一共倒 n 次(每次倒一个单位体积,正好倒完),第 j 次倒果汁只能倒进第 aj 或 aj+1 个水杯。具体的,如果第 aj 和 aj+1 两个水杯都不是满的,那么可以任意倒入其中一个;如果有且仅有一个不是满的,那么只能倒入不是满的的那一个;如果都已经被倒满了,那么小奶龙就可以开心地喝掉这一个单位体积的果汁。并且为了方便,小可可会安排小奶龙从左向右倒果汁,也就是说对于所有 1≤j<n,保证 aj≤aj+1。
小奶龙想知道自己最多能喝掉单位体积的果汁,你能帮帮他吗?
你一共需要解决 T 组测试数据。
思路:
首先读完题,结合样例解释后,一眼贪心,我想了10分钟,想到了一种大致的贪心策略:如果第x杯与第(x+1)杯都没有装满果汁,将1各单位的果汁装进第(x+1)杯中;如果第 x+1 杯被装满了,装第 x 杯中 ;如果第 x 杯被装满了,装第 x+1 杯中 ;否则,只能喝一个单位的果汁了。
证明过程:因为题目说了 a 数组是一个单调递增的,所以如果我们倒第x 杯中,那么后面一个的就到不了,这样果汁就会分散在一些杯子中,所以,必定不划算;反之,果汁就会集中在一些靠后的杯子中,到了后期,必定有一些果汁是倒不了,此时答案才能是最大化的。
代码实验不难,以下代码仅供参考:
#include<bits/stdc++.h>
using namespace std;
#define int long long
int t;
int n, m;
int v[100001];
int a[1000001];
int ans;
signed main() {
// freopen("juice2.in","r",stdin);
// freopen("juice2.out","w",stdout);
cin >> t;
while (t--) {
ans = 0;
cin >> n >> m;
for (int i = 1; i <= m; i++) cin >> v[i];
for (int i = 1; i <= n; i++) cin >> a[i];
for (int i = 1; i <= n; i++) {
// cout << v[a[i]] << " " << v[a[i] + 1] << endl;
if (v[a[i]] && v[a[i] + 1]) v[a[i] + 1]--;
else if(!v[a[i]] && v[a[i] + 1]) v[a[i] + 1] --;
else if (v[a[i]] && !v[a[i] + 1]) v[a[i]]--;
else ans++;
}
cout << ans << endl;
}
return 0;
}
T3:前置知识:拓扑排序,bitset
如果不了解bitset,可以在oi-wiki看。
声明:此题中的或运算用 or 表示
题目原文:
题目描述
小可可来到了 P 国旅行。
P 国共有 n 个城市和 m 条有向道路,其中第 i 条道路为从城市 ui 到城市 vi,长度为 wi。保证 ui<vi。
对于一次游览,假设小可可依次经过了城市 x1,x2,…,xk,其中从 xi 到 xi+1 经过的路径长度为 yi。记 si 表示 y1,y2,…,yi 的按位或值。那么小可可认为这次游览就是从 x1 走到 xk,疲劳度就是 mex(s1,s2,…,sk−1)。
其中 mex(a1,a2,…,an) 表示最小的没有出现在 a1,a2,…,an 中的自然数。例如 mex(0,2,3)=1,mex(1,4)=0,mex(0,1,2,3,4)=5。
小可可认为两座城市 s,t 之间的距离为他从 s 走到 t 所有可能的游览方案中疲劳度的最大值,记作 d(s,t)。如果不能从 s 走到 t,那么 d(s,t)=−1。规定 d(s,s)=0。现在他想要知道任意两座城市之间的距离之和,即 $$\displaystyle \sum_{i=1}^{n} \sum_{j=1}^{n} d(i, j)$$
思路:
首先我们能发现一些性质:
命题:这题的 mex 值只能为 0,1,2。
证明:如果 mex>2,那么 mex 一定有 0,1,2 的前缀,但是有 2 的一项一定是从 1 or 过来的,1 or x = 2,无解,因为无论x取什么值,1 or x 只能等于 1 ,故原命题是真命题。
3 种情况:
1:从某个点出发,第一条边边权不是 0。那么后面不管怎么走,得到的 mex 组成的序列单调不降,第一条边的边权就是最小值,序列中不可能出现 0,所以 mex 是 0。
2:从某个点出发,第一条边边权是 0,第一条不是 0 的边边权不是 1,或这条链上根本没有边权不是 0 的边。同情况 1,此时序列中不可能出现 1,mex 是 1。
3:如果从某个点出发,第一条边边权是 0,第一条不是 0 的边边权是 1,那么显然疲劳度序列中要么是 0,要么由于按位或 1 而是偶数,mex 为 2。
而题目给的 d(s,t)函数分如下情况:
1:d(s,t)=-1 时,我们可以在原图的基础上先存一个反边,然后用拓扑排序求出每个点可以到达哪些点,这一步骤使用 bitset 将很好实现。这样,我们就可以统计出有多少条无法到达的路径,这样的路径的疲劳度是 −1。
2:d(s,t)=0 时,对答案无贡献,不计算。
3:d(s,t)=1 时,
这个很好理解。对于一个点 u,如果有一条通向点 v 的边且边权为 0,那么 u 与 v 所有可以到达的点的路径疲劳度都至少为 1,因为这里可能包含着情况 3。
遍历所有的边,对于所有边权为 0 的边进行统计即可。
4:d(s,t)=2 时,首先,统计出每个点通过一条满足第一条边的边权为 1 的路径可以到达的点的数量。然后,统计有每个点只通过边权为 0 的边可以到达的点,假定有一点 u,可以通过一条边权全部为 0 的路径到达点 v,那么 u 与所有满足 v 通过一条满足第一条边的边权为 1 的路径可以到达的点之间的路径疲劳度为 2。
将答案累加输出即可。
代码仅供参考:
#include<bits/stdc++.h>
using namespace std;
#define int long long
int ans;
bitset<30004>get1[30004];//表示从城市 i 可以直接或间接到达的所有城市的集合,使用 bitset 存储,方便进行位运算
vector<pair<int, int>>g[30004];/*是一个邻接表,
/存储从城市 i 出发的所有有向边,pair 中的第一个元素是目标城市,第二个元素是边的长度/*/
bitset<30004>zero[30004]//表示从城市 i 出发,经过长度为 0 的边可以直接到达的城市集合(mex=0)
, one[30004], // 表示从城市 i 出发,经过长度为 1 的边可以直接或间接到达的城市集合(mex=1)
two[30004];//表示从城市 i 出发,经过长度为 2 的边可以直接或间接到达的城市集合(mex=1)
signed main() {
int n, m;
cin >> n >> m;
for (int i = 1; i <= m; i++) {
int u, v, w;
cin >> u >> v >> w;
get1[u][v] = 1;//标记从城市 u 可以到达城市 v
if (w == 0) zero[u][v] = 1;//标记从城市 u 经过长度为 0 的边可以到达城市 v
if (w == 1) one[u][v] = 1;//标记从城市 u 经过长度为 1 的边可以到达城市 v
g[u].push_back({v,w});//边 (u, v, w) 添加到邻接表 g 中
}
ans = 1 - n;//为了在后续计算中更方便、准确地处理自环和不可达情况
for (int i = n - 1; i >= 1; i--) {//从倒数第二个城市开始,逆序处理每个城市
// 遍历从城市 i 出发的所有边
for (auto it:g[i]) {
int v = it.first//获取当前边的目标城市 v
, w = it.second;//获取当前边的长度 w
get1[i] |= get1[v];
if (w == 0)// i 与 j 之间的边权为 0
{
two[i] |= one[v];
zero[i] |= get1[v];
one[i] |= one[v];
}
else if (w == 1)
one[i] |= get1[v];// 这里统计了每一个点通过一条满足第一条边的边权为 1 的路径可以到达哪些点。
}
//减去从 i 无法到达的城市对结果的贡献
ans -= (n - 1) - get1[i].count();
ans += two[i].count();
two[i] |= zero[i];
ans += two[i].count();
}
cout << ans;
return 0;
}
/*
*/
T4:不会,鸽了。。。
总结:
T3 我觉得出的挺好的,自己 vp 的时候,没有思路,打了暴力,‘’荣获‘’ 25 分,看了题解后,过了很久才理解。T1,T2 还是挺常规的。
VP 总分:100+100+25=225 分,不知道是否能上2+?