ABC429题解
A - Too Many Requests
问题描述
给定两个正整数 $ N $ 和 $ M $,请输出 $ N $ 行。
第 $ i $ 行($ 1 \leq i \leq N $)应包含:如果 $ i \leq M $,则输出 OK;否则输出 Too Many Requests。
约束条件
- $ 1 \leq N \leq 10 $
- $ 1 \leq M \leq 10 $
- $ N $ 和 $ M $ 是整数。
输入
输入通过标准输入给出,格式如下:
输出
根据问题描述中的要求输出 $ N $ 行。
代码
#include<stdio.h>
#include<iostream>
using namespace std;
int main() {
int n, m;
cin >> n >> m;
for(int i = 1; i <= n; i++){
if(i <= m) cout << "OK" << endl;
else cout << "Too Many Requests" << endl;
}
return 0;
}
B - N - 1
问题描述
给定一个长度为 $ N $ 的整数序列 $ A = (A_1, A_2, \ldots, A_N) $ 和一个整数 $ M $。
判断是否可以通过移除 $ A $ 中的一个元素,使得剩下的 $ N-1 $ 个元素之和恰好等于 $ M $。
约束条件
- $ 2 \leq N \leq 100 $
- $ 0 \leq M \leq 10000 $
- $ 0 \leq A_i \leq 100 $
- 所有输入值均为整数
输入
输入通过标准输入给出,格式如下:
输出
如果可以通过移除 $ A $ 中的一个元素使得剩下的 $ N-1 $ 个元素之和恰好等于 $ M $,则输出 Yes;否则输出 No。
样例一
输入
4 10
3 2 3 4
输出
Yes
样例二
输入
5 16
3 3 4 2 5
输出
No
样例三
输入
6 16
0 8 0 2 6 8
输出
Yes
代码
#include<stdio.h>
#include<iostream>
using namespace std;
int a[105];
int main() {
int n, m;
cin >> n >> m;
int sum = 0;
for(int i = 1; i <= n; i++){
cin >> a[i];
sum += a[i];
}
if(sum < m){
cout << "No" << endl;
return 0;
}
for(int i = 1; i <= n; i++){
if(sum - a[i] == m){
cout << "Yes" << endl;
return 0;
}
}
cout << "No" << endl;
return 0;
}
C - Odd One Subsequence
问题描述
给定一个长度为 $ N $ 的整数序列 $ A = (A_1, A_2, \ldots, A_N) $。
找出满足 $ 1 \leq i < j < k \leq N $ 的整数三元组 $ (i, j, k) $ 的数量,且这些三元组满足以下条件:
- $ A_i, A_j, A_k $ 中恰好包含两个不同的值。也就是说,$ A_i, A_j, A_k $ 中有两个相等,另一个不同。
约束条件
- $ 3 \leq N \leq 2 \times 10^5 $
- $ 1 \leq A_i \leq N $
- 所有输入值均为整数
输入
输入通过标准输入给出,格式如下:
输出
输出满足条件的三元组的数量。
样例输入 1
5
3 2 5 2 2
样例输出 1
6
例如,三元组 $ (i, j, k) = (1, 2, 4) $ 满足条件,因为在 $ A_1 = 3, A_2 = 2, A_4 = 2 $ 中恰好包含两个不同的值:2 和 3。包括这个在内,共有六个三元组满足条件:$ (i, j, k) = (1, 2, 4), (1, 2, 5), (1, 4, 5), (2, 3, 4), (2, 3, 5), (3, 4, 5) $。
因此,输出 6。
样例输入 2
3
1 1 1
样例输出 2
0
可能不存在满足条件的三元组。
代码
用一个计数数组\(cnt\)统计下所有元素的个数。
那么\(cnt[i]\)对答案的贡献即为\(cnt[i] * (cnt[i] - 1) / 2 * (n - cnt[i])\)
#include<stdio.h>
#include<iostream>
using namespace std;
const int N = 200005;
typedef long long ll;
ll a[N];
ll cnt[N], cnt1[N],len[N];
int main() {
ll n, ans = 0;
cin >> n;
for(ll i = 1; i <= n; i++){
cin >> a[i];
cnt[a[i]] ++;
}
for(int i = 1; i <= n; i++)
ans += cnt[i] * (cnt[i] - 1) / 2 * (n - cnt[i]);
cout << ans << endl;
return 0;
}
D - On AtCoder Conference
问题描述
有一个周长为 $ M $ 的圆形池塘,池塘边有一座小屋和 $ N $ 个人。
对于一个实数 $ x ( 0 \leq x < M )$,定义点 $ x $ 为从小屋位置开始顺时针移动 $ x $ 距离所到达的位置。
第 $ i $ 个人位于点 $ A_i $。多个人员可能站在同一位置。
另外,给定一个不超过 $ N $ 的整数 $ C $。对于 $ i = 0, 1, \ldots, M - 1 $,按如下方式定义 $ X_i $:
- 高桥从点 $ (i + 0.5) $ 出发,开始顺时针移动。
- 只要他遇到的人员总数小于 $ C $,高桥就继续(顺时针)移动,当遇到的人数达到或超过 $ C $ 时停止。这里,"在点 $ y $ 遇到一个人" 指的是高桥到达了点 $ y $。
- 令 $ X_i $ 为高桥在停止前遇到的人员数量。这里,如果高桥停止的点上有多个人员,则该点上的所有人员都被计入他遇到的人数中,特别地,$ X_i $ 可能大于 $ C $。
请计算所有 $ i = 0, 1, \ldots, M - 1 $ 对应的 $ X_i $ 的总和,即:\(\sum_{i=0}^{M-1} X_i\)
约束条件
- $ 1 \leq N \leq 5 \times 10^5 $
- $ 1 \leq M \leq 10^{12} $
- $ 0 \leq A_i \leq M - 1 $
- $ 1 \leq C \leq N $
所有输入值均为整数。
输入
输入通过标准输入给出,格式如下:
输出
在一行中输出所有 $ i = 0, 1, \ldots, M - 1 $ 对应的 $ X_i $ 的总和。
样例输入 1
5 3 2
1 2 1 0 1
样例输出 1
9
样例解释 1
当 $ i = 0 $ 时,高桥从点 0.5 出发并顺时针移动。随后发生以下情况:
- 在点 1,他遇到了第 1、3、5 个人,总共三人,此时遇到的总人数为 3。这不小于 $ C = 2 \(,因此高桥在那里停止。所以,\) X_0 = 3 $。
当 $ i = 1 $ 时,高桥从点 1.5 出发并顺时针移动。随后发生以下情况:
- 在点 2,他遇到了第 2 个人。此时遇到的总人数为 1,因此他继续移动。
- 在点 0,他遇到了第 4 个人,此时遇到的总人数为 2。这不小于 $ C = 2 \(,因此高桥在那里停止。所以,\) X_1 = 2 $。
当 $ i = 2 $ 时,高桥从点 2.5 出发并顺时针移动。随后发生以下情况:
- 在点 0,他遇到了第 4 个人。此时遇到的总人数为 1,因此他继续移动。
- 在点 1,他遇到了第 1、3、5 个人,总共三人,此时遇到的总人数为 4。这不小于 $ C = 2 \(,因此高桥在那里停止。所以,\) X_2 = 4 $。
因此,答案是 $ X_0 + X_1 + X_2 = 3 + 2 + 4 = 9 $。
样例输入 2
1 10000000000000 1
1
样例输出 2
10000000000000
无论从哪个起始位置开始,高桥都会在遇到池塘边唯一的那个人(位于点 1)时停止。
因此,无论 $ i $ 取何值,$ X_i = 1 $,所以答案是 $ 10^{12} $。
代码
思路
如果把环拉直看作是一个无限长的序列,那么题意可以转换为:
预处理前缀和数组\(s[i]\),单个环的总人数可以用\(s[m]\)表示。
对于给定\(C\),找到变量\(x\)和\(i\)满足:\(x*s[m]+s[i]-C\)的值最小。
当移动初始位置到点\(t\)时,等价于公式转换成\(x*s[n]+s[i-1]-s[t]-C\)
可以用二分的方法查找该公式对应的最小值。
代码实现
- 数据离散化。周长为\(M\),数据最大值为\(10^{12}\),但是人数\(N\)最多为\(5*10^5\),所以要进行离散化操作。处理输入数据,先用map纪录每个点上的人数,在用struct结构存下来,用sort按照坐标大小排序,按照题目可得\(cnt\)最大值不会超过\(5*10^5\);
- 处理所有坐标位置对答案的贡献。原来周长为\(M\),每个坐标点都有贡献。现在只剩下\(cnt\)个坐标点。但是对于中间没有人在的空的坐标点,其对答案的贡献和顺时针走到的第一个有人的坐标点是一样的。因此对于每一个有人在的坐标点\(x\)而言,如果原本对答案的贡献值为\(val\),但是在\(x\)前面还有\(t\)个无人的坐标点,那么等价于坐标点\(x\)对答案的贡献值为\((t+1)*val\),从而降低时间复杂度;
- 处理单个坐标位置对答案的贡献。利用\(s\)数组先预处理出坐标点人数的前缀和。对于整个环的总和\(s[idx]\)而言,每一个坐标点会绕\(c / s[idx]\)圈环,\(m * (c / s[idx] * s[idx])\)代表所有坐标点绕整数圈环对结果的贡献。再对剩余部分\(c \% s[idx]\)进行计算。考虑到模拟环的结构,再将前缀和数组扩大两倍。对于坐标点\(x\),在范围\(x\)到范围\(x+idx-1\)之间进行二分,找到在该前缀和数组范围内第一个满足值\(>=c \% s[idx]\)的位置,乘上第二步得到的贡献值权重,即为该位置对答案的贡献。将所有贡献值累计即为结果,最终时间复杂度为\(O(nlogn)\)。
#include<stdio.h>
#include<iostream>
#include<algorithm>
#include<map>
using namespace std;
typedef long long ll;
const int N = 500005;
map<ll, ll> mp;
int idx = 0;
ll s[2 * N];
ll n, m, c;
struct node{
ll val, cnt, interval;
}d[2 * N];
bool cmp(node a, node b){
return a.val < b.val;
}
ll erfen(int x){
ll l = x - 1, r = x + idx - 1;
while(l < r){
ll mid = (l + r) / 2;
if(s[mid] - s[x-1] >= c) r = mid;
else l = mid + 1;
}
return s[l] - s[x-1];
}
int main() {
ll ans = 0, t;
cin >> n >> m >> c;
for(int i = 1; i <= n; i++){
cin >> t;
if(!mp[t]) mp[t] = 1;
else mp[t] ++;
}
for(auto& t : mp){
d[++idx].val = t.first;
d[idx].cnt = t.second;
}
sort(d+1, d+idx+1, cmp);
d[1].interval = d[1].val + (m - d[idx].val);
for(int i = 2; i <= idx; i++) d[i].interval = d[i].val - d[i-1].val;
for(int i = 1; i <= idx; i++) s[i] = s[i-1] + d[i].cnt;
for(int i = 1; i <= idx; i++) s[i+idx] = s[i+idx-1] + d[i].cnt;
ans += m * (c / s[idx] * s[idx]);
c %= s[idx];
for(int i = 1; i <= idx; i++) ans += d[i].interval * erfen(i);
cout << ans << endl;
return 0;
}
E - Hit and Away
题目描述
给定一个简单连通无向图 \(G\),包含 \(N\) 个顶点和 \(M\) 条边。
图 \(G\) 的顶点和边分别编号为顶点 \(1, 2, \ldots, N\) 和边 \(1, 2, \ldots, M\)。边 \(i\) 连接顶点 \(U_i\) 和 \(V_i\)。
你可以沿着边在顶点之间双向移动,每次移动花费时间为 \(1\)。
此外,每个顶点要么是安全的,要么是危险的。这个状态由一个长度为 \(N\) 的字符串 \(S\) 给出,字符串仅包含字符 S 和 D。
具体来说,当 \(S\) 的第 \(i\) 个字符(\(1 \leq i \leq N\))是 S 时,顶点 \(i\) 是安全的;当它是 D 时,顶点 \(i\) 是危险的。
保证图中至少有两个安全顶点和至少一个危险顶点。
对于每个危险顶点 \(v\),请找出以下的值:
- 从某个安全顶点出发,经过 \(v\),最后到达一个不同于出发点的安全顶点所需的最小可能时间。
约束条件
- \(3 \leq N \leq 2 \times 10^5\)
- \(N - 1 \leq M \leq 2 \times 10^5\)
- \(1 \leq U_i, V_i \leq N\)
- \(U_i \neq V_i\)
- 如果 \(i \neq j\),则 \(\{U_i, V_i\} \neq \{U_j, V_j\}\)
- \(S\) 是一个长度为 \(N\) 的字符串,仅由字符
S和D组成。 - \(N, M, U_i, V_i\) 均为整数。
- 图 \(G\) 是连通的。
- 图中至少有两个安全顶点。
- 图中至少有一个危险顶点。
输入格式
输入从标准输入按以下格式给出:
N M
U₁ V₁
U₂ V₂
⋮
U_M V_M
S
输出格式
设 \(K\) 为图 \(G\) 中危险顶点的数量,输出 \(K\) 行。
在第 \(i\) 行(\(1 \leq i \leq K\)),按照顶点编号升序排列的危险顶点,输出第 \(i\) 个危险顶点对应的答案。
样例输入 1
5 5
1 2
1 3
2 3
3 4
4 5
SSDDS
样例输出 1
2
3
危险顶点(按顶点编号升序排列)是顶点 3 和顶点 4。
对于顶点 3,从顶点 1 → 顶点 3 → 顶点 2(举例)的移动满足条件。
此移动所需时间为 2,且是最小值。
因此,在第 1 行输出 2。
对于顶点 4,从顶点 1 → 顶点 3 → 顶点 4 → 顶点 5(举例)的移动满足条件。
此移动所需时间为 3,并且不存在满足条件且时间在 2 或以下的移动方式,因此这是最小值。
所以,在第 2 行输出 3。
样例输入 2
3 2
1 2
2 3
SSD
样例输出 2
3
危险顶点是顶点 3。
从顶点 1 → 顶点 2 → 顶点 3 → 顶点 2(举例)的移动满足条件。
此移动所需时间为 3,且是最小值。
请注意,像顶点 2 → 顶点 3 → 顶点 2 这样的移动不满足"终点必须不同于起点"的条件。
代码
思路
因为图为无向图并且点和点之间的距离都为1,所以题意转换成:对于每个危险顶点\(x\),找到距离\(x\)最近的安全顶点\(y1\)和次近的安全定点\(y2\),那么\(x\)对答案的贡献值即为\(dis(x,y1)+dis(x,y2)\)。
对于每个安全起点入队,初始距离为0,对于每个危险起点,最短距离和次短距离设为INF
通过BFS求解,每次不断扩散,入队时格式为{int, int},其中第一个值代表当前节点。第二个值代表走到当前节点花费的距离。最后统计结果。
还是wa了。。。哪里的问题。。。
//abc479E
#include<stdio.h>
#include<iostream>
#include<vector>
#include<queue>
using namespace std;
typedef pair<int, int> PII;
const int N = 200005;
vector<int> edge[N];
queue<PII> q;
int min_dis[N], min2_dis[N];
bool flag[N];
int main() {
int n, m;
int u, v;
cin >> n >> m;
for(int i = 1; i <= m; i++){
cin >> u >> v;
edge[u].push_back(v);
edge[v].push_back(u);
}
char ch;
ch = getchar();
for(int i = 1; i <= n; i++){
//init
min_dis[i] = 0x3f3f3f;
min2_dis[i] = 0x3f3f3f;
ch = getchar();
if(ch == 'S'){
q.push({i, 0});
min_dis[i] = 0;
flag[i] = 1;
}
}
//bfs
while(!q.empty()){
PII t = q.front();
q.pop();
int num = t.first;
int step = t.second;
for(int i = 0; i < edge[num].size(); i++){
int x = edge[num][i];
if(step + 1 <= min_dis[x]){
min2_dis[x] = min_dis[x];
min_dis[x] = step + 1;
q.push({x,step+1});
}
else if(step + 1 < min2_dis[x]){
min2_dis[x] =step + 1;
q.push({x,step+1});
}
//if(min2_dis[num] + 1 < min2_dis[x]) min2_dis[x] = min2_dis[num] + 1;
}
}
for(int i = 1; i <= n; i++) if(!flag[i]) cout << min_dis[i] + min2_dis[i] << endl;
return 0;
}

浙公网安备 33010602011771号