ACM2021辽宁省赛:CDEFGILM
C-传染病统计_
问题解析
一开始没注意到n只有8。。。当1e5的范围来写的。
这题就是说,所有相隔距离小于等于2的是一个集体,然后我们要选一个集体感染病毒,问最多的感染人数和最小的感染人数。
先对所有人进行升序排序(因为不知道给的输入是不是有序的)。然后遍历一遍数组,把相隔距离不超过2的都当作一个集体,如果B和A的距离超过2了,说明B是另一个集体,我们重新计算。在此过程中维护集体人数的最大值和最小值。
分别输出最小值和最大值即可。
时间复杂度:(T*nlogn);
AC代码
#include<iostream>
using namespace std;
#include<vector>
#include<algorithm>
#include<math.h>
#include<set>
#include <random>
#include<numeric>
#include<string>
#include<string.h>
#include<iterator>
#include<fstream>
#include<map>
#include<unordered_map>
#include<stack>
#include<list>
#include<queue>
#include<iomanip>
#include<bitset>
#pragma GCC optimize(2)
#pragma GCC optimize(3)
#define endl '\n'
#define int ll
#define PI acos(-1)
#define INF 0x3f3f3f3f
typedef long long ll;
typedef unsigned long long ull;
typedef pair<ll, ll>PII;
const int N = 1e6 + 50;
void solve()
{
int n;
cin >> n;
vector<int>v(n);
for (int i = 0; i < n; i++)cin >> v[i];
sort(v.begin(), v.end());
int mx = 0, mn = 1e9, len = 1;
for (int i = 1; i < n; i++)
{
if (v[i] - v[i - 1] <= 2)len++;
else
{
mx = max(mx, len);
mn = min(mn, len);
len = 1;
}
}
mx = max(mx, len);
mn = min(mn, len);
cout << mn << " " << mx << endl;
}
signed main()
{
ios_base::sync_with_stdio(false);
cin.tie(nullptr);
cout.tie(nullptr);
int t = 1;
cin >> t;
while (t--)
{
solve();
}
return 0;
}
D-阿强与网格
问题解析
一开始下意识的想BFS,后来发现根本不用那么麻烦(而且也过不了)。
题目已经给定了我们每次操作所花成本,我们可以根据x和y的大小,与网格的形状直接计算得出结果,为了方便,我们认定n比m大:
- 如果网格是n *1的形状,此时显然只能用x的方式移动,成本为:*(n-1)x;
- 因为y走一步等于x走两步,所以如果x的两倍小于等于y,我们直接用x走,成本为:*(n+m-2)x
剩下的情况,我们显然是能用y走就用y走,问题是y该怎么走呢?
一开始想着先对角线移动到底部,然后剩下的用x平移过去:(红色路线)
后来发现,其实剩下的位置也是可以斜着走的:(蓝色路线)
只不过这么走有约束,即到了底边后,距离终点的距离如果是奇数,则会停在终点的前一格,而这时我们只能用x走了:(橙色路线)
那么对于其它的情况:我们先走m次y,如果距离终点的距离:n-m是奇数,我们走了(n-m-1)次y后,还要走一次x,成本为:m*y+(n-m-1) * y+x;如果n-m是偶数,我们直接全走y即可,成本为:m*y+(n-m) * y.
AC代码
#include<iostream>
using namespace std;
#include<vector>
#include<algorithm>
#include<math.h>
#include<set>
#include <random>
#include<numeric>
#include<string>
#include<string.h>
#include<iterator>
#include<fstream>
#include<map>
#include<unordered_map>
#include<stack>
#include<list>
#include<queue>
#include<iomanip>
#include<bitset>
#pragma GCC optimize(2)
#pragma GCC optimize(3)
#define endl '\n'
#define int ll
#define PI acos(-1)
#define INF 0x3f3f3f3f
typedef long long ll;
typedef unsigned long long ull;
typedef pair<ll, ll>PII;
const int N = 4e5 + 50;
void solve()
{
int n, m, x, y,ans;
cin >> n >> m >> x >> y;
//为了方便,我们保证n比m大
if(n<m)swap(n,m);
if(m==1)ans=(n-1)*x;
else if(x*2<=y)ans=(n+m-2)*x;
else if(x<=y)ans=(m-1)*y+(n-m)*x;
else
{
if((n-m)%2==1)ans=(n-2)*y+x;
else ans=(n-1)*y;
}
cout<<ans<<endl;
}
signed main()
{
ios_base::sync_with_stdio(false);
cin.tie(nullptr);
cout.tie(nullptr);
int t = 1;
cin >> t;
while (t--)
{
solve();
}
return 0;
}
E-生活大爆炸
问题解析
这道题主要思路就是我们高中就学过的组合数计算。
男生有n人,女生有m人,需要组成总人数为t的队伍,其中男生人数不能少于4人,女生人数不能少于1人。
那么可能的排列组合就是:4个男生和t-4名女生,5个男生和t-5名女生,……,t-1名男生和1名女生。
对于第一个组合来说,就是n个男生中随机选出4个,搭配上m个女生中随机选出t-4个,即:C(n,4)*C(m,t-4)。其余的以此类推,把所有的组合数相加即可。
AC代码
#include<iostream>
using namespace std;
#include<vector>
#include<algorithm>
#include<math.h>
#include<set>
#include <random>
#include<numeric>
#include<string>
#include<string.h>
#include<iterator>
#include<fstream>
#include<map>
#include<unordered_map>
#include<stack>
#include<list>
#include<queue>
#include<iomanip>
#include<bitset>
#pragma GCC optimize(2)
#pragma GCC optimize(3)
#define endl '\n'
#define int ll
#define PI acos(-1)
#define INF 0x3f3f3f3f
typedef long long ll;
typedef unsigned long long ull;
typedef pair<ll, ll>PII;
const int N = 4e5 + 50;
int C[70][70];
void init()
{
for (int i = 0; i <= 70; i++)
for (int j = 0; j <= i; j++) {
if (!j)
C[i][j] = 1;
else
C[i][j] = C[i - 1][j - 1] + C[i - 1][j];
}
}
void solve()
{
int n, m, t;
cin >> n >> m >> t;
init();
int res = 0;
for (int i = 4; i <= min(n, t - 1); i++)
{
res += C[n][i] * C[m][t - i];
}
cout << res;
}
signed main()
{
ios_base::sync_with_stdio(false);
cin.tie(nullptr);
cout.tie(nullptr);
int t = 1;
//cin >> t;
while (t--)
{
solve();
}
return 0;
}
F-Capslock
问题解析
只要看第2到第n个字符是不是都是大写字母,如果是,遍历一遍数组把大写字母变成小写字母,把小写字母变成大写字母。如果不是,直接原样输出。
AC代码
#include<iostream>
using namespace std;
#include<vector>
#include<algorithm>
#include<math.h>
#include<set>
#include <random>
#include<numeric>
#include<string>
#include<string.h>
#include<iterator>
#include<fstream>
#include<map>
#include<unordered_map>
#include<stack>
#include<list>
#include<queue>
#include<iomanip>
#include<bitset>
#pragma GCC optimize(2)
#pragma GCC optimize(3)
#define endl '\n'
#define int ll
#define PI acos(-1)
#define INF 0x3f3f3f3f
typedef long long ll;
typedef unsigned long long ull;
typedef pair<ll, ll>PII;
const int N = 1e6 + 50;
void solve()
{
string s;
cin >> s;
int n = s.size();
bool flag = true;
for (int i = 1; i < n; i++)
{
if (!(s[i] >= 'A' && s[i] <= 'Z'))
{
flag = false;
break;
}
}
if (!flag)
{
cout << s << endl;
return;
}
if (s[0] >= 'A' && s[0] <= 'Z')
{
s[0] += 32;
}
else s[0] -= 32;
for (int i = 1; i < n; i++)
{
s[i] += 32;
}
cout << s;
}
signed main()
{
ios_base::sync_with_stdio(false);
cin.tie(nullptr);
cout.tie(nullptr);
int t = 1;
//cin >> t;
while (t--)
{
solve();
}
return 0;
}
G-字节类型
问题解析
可以用字符串来接收输入。
根据字符串的长度和转换成整数后的大小进行比较即可。
如果担心longlong会炸,就用unsigned long long来存储结果。字符串长度要是大于19,直接就是BigInteger。
AC代码
#include<iostream>
using namespace std;
#include<vector>
#include<algorithm>
#include<math.h>
#include<set>
#include <random>
#include<numeric>
#include<string>
#include<string.h>
#include<iterator>
#include<fstream>
#include<map>
#include<unordered_map>
#include<stack>
#include<list>
#include<queue>
#include<iomanip>
#include<bitset>
#pragma GCC optimize(2)
#pragma GCC optimize(3)
#define endl '\n'
#define int ll
#define PI acos(-1)
#define INF 0x3f3f3f3f
typedef long long ll;
typedef unsigned long long ull;
typedef pair<ll, ll>PII;
const int N = 1e6 + 50;
ull check(string s)
{
ull num = 0;
int n = s.size();
for (int i = 0; i < n; i++)
{
num *= 10;
num += s[i] - '0';
}
return num;
}
void solve()
{
string s;
cin >> s;
int n = s.size();
if (n > 19)cout << "BigInteger" << endl;
else if (n <= 3)
{
int num = stoi(s);
if (num <= 127)cout << "byte" << endl;
else cout << "short" << endl;
}
else if (n <= 5)
{
int num = stoi(s);
if (num <= 32767)cout << "short" << endl;
else cout << "int" << endl;
}
else if (n <= 10)
{
int num = stoi(s);
if (num <= 2147483647)cout << "int" << endl;
else cout << "long" << endl;
}
else if (n <= 19)
{
ull num = check(s);
if (num <= 9223372036854775807)cout << "long" << endl;
else cout << "BigInteger" << endl;
}
}
signed main()
{
ios_base::sync_with_stdio(false);
cin.tie(nullptr);
cout.tie(nullptr);
int t = 1;
//cin >> t;
while (t--)
{
solve();
}
return 0;
}
I-完美主义
问题解析
开一个单点修改+区间查询的线段树。
- st[k]表示,st[k]为true,表示当前区间是完美区间,反之不是;
- right[k]表示当前区间最右边的元素;
- left[k]表示当前区间最右边的元素;
对于一个区间,如果它的左右子区间的st都为true,且左区间的right小于等于右区间的left,那么当前区间也是一个true;反之是false。由此类推,我们每次更新时,只要维护区间的left和right,再根据他们的大小判断st的值,就可以快速得出当前区间是否是完美的。
AC代码
#include<iostream>
using namespace std;
#include<vector>
#include<algorithm>
#include<math.h>
#include<set>
#include <random>
#include<numeric>
#include<string>
#include<string.h>
#include<iterator>
#include<fstream>
#include<map>
#include<unordered_map>
#include<stack>
#include<list>
#include<queue>
#include<iomanip>
#include<bitset>
#pragma GCC optimize(2)
#pragma GCC optimize(3)
#define endl '\n'
#define int ll
#define PI acos(-1)
#define INF 0x3f3f3f3f
typedef long long ll;
typedef unsigned long long ull;
typedef pair<ll, ll>PII;
const int N = 4e5 + 50;
int myleft[4 * N], myright[4 * N], a[N];
bool st[4 * N];
void build_tree(int k, int l, int r)
{
if (l == r)
{
myleft[k] = a[l];
myright[k] = a[l];
st[k] = true;
return;
}
int m = (l + r) / 2;
build_tree(k + k, l, m);
build_tree(k + k + 1, m + 1, r);
if (myleft[k + k + 1] >= myright[k + k])
{
st[k] = st[k + k] && st[k + k + 1];
}
else st[k] = false;
myleft[k] = myleft[k + k];
myright[k] = myright[k + k + 1];
}
void revise(int k, int l, int r, int x, int y)
{
if (l == r)
{
myleft[k] = y;
myright[k] = y;
return;
}
int m = (l + r) / 2;
if (x <= m)revise(k + k, l, m, x, y);
else revise(k + k + 1, m + 1, r, x, y);
if (myleft[k + k + 1] >= myright[k + k])
{
st[k] = st[k + k] && st[k + k + 1];
}
else st[k] = false;
myleft[k] = myleft[k + k];
myright[k] = myright[k + k + 1];
}
//对于pair<bool, pair<int, int>>,first表示这个区间的st。
//second。first是left;second。second是right
pair<bool, pair<int, int>> calc(int k, int l, int r, int x, int y)
{
if (l == x && r == y)return { st[k],{myleft[k],myright[k]} };
int m = (l + r) / 2;
if (y <= m)return calc(k + k, l, m, x, y);
else
if (x > m)return calc(k + k + 1, m + 1, r, x, y);
else
{
auto ans1 = calc(k + k, l, m, x, m);
auto ans2 = calc(k + k + 1, m + 1, r, m + 1, y);
if (ans1.first == false || ans2.first == false)
{
return { false,{-1,-1} };
}
else
{
if (ans1.second.second <= ans2.second.first)
{
return { true,{ans1.second.first,ans2.second.second} };
}
else return { false,{-1,-1} };
}
}
}
void solve()
{
int n, m, t, x, y;
cin >> n >> m;
for (int i = 1; i <= n; i++)cin >> a[i];
build_tree(1, 1, n);
while (m--)
{
cin >> t >> x >> y;
if (t == 1)
{
revise(1, 1, n, x, y);
}
else
{
if (calc(1, 1, n, x, y).first)
{
cout << "Yes" << endl;
}
else cout << "No" << endl;
}
}
}
signed main()
{
ios_base::sync_with_stdio(false);
cin.tie(nullptr);
cout.tie(nullptr);
int t = 1;
//cin >> t;
while (t--)
{
solve();
}
return 0;
}
L-神奇的回答
问题解析
大于18的输出18,小于等于18的原样输出。
AC代码
#include<iostream>
using namespace std;
#include<vector>
#include<algorithm>
#include<math.h>
#include<set>
#include <random>
#include<numeric>
#include<string>
#include<string.h>
#include<iterator>
#include<fstream>
#include<map>
#include<unordered_map>
#include<stack>
#include<list>
#include<queue>
#include<iomanip>
#include<bitset>
#pragma GCC optimize(2)
#pragma GCC optimize(3)
#define endl '\n'
#define int ll
#define PI acos(-1)
#define INF 0x3f3f3f3f
typedef long long ll;
typedef unsigned long long ull;
typedef pair<ll, ll>PII;
const int N = 1e6 + 50;
void solve()
{
int n;
cin >> n;
if (n >= 18)cout << 18 << endl;
else cout << n << endl;
}
signed main()
{
ios_base::sync_with_stdio(false);
cin.tie(nullptr);
cout.tie(nullptr);
int t = 1;
cin >> t;
while (t--)
{
solve();
}
return 0;
}
M-比赛!
问题解析
拓扑排序。
根据每个人的回答,我们把它看成“A:BC”,转化成关系就是,B->A->C。我们以此来建图,同时记录每个点的入度。
建图之后来一遍拓扑排序,用一个字符串s来存储结果,因为入度为0的点,相当于它的前面没有别的选手,我们就可以把它加入到字符串的尾部。
如果拓扑排序后,所有的选手都在字符串里了,我们就把他输出;否则输出No Answer。
AC代码
#include<iostream>
using namespace std;
#include<vector>
#include<algorithm>
#include<math.h>
#include<set>
#include <random>
#include<numeric>
#include<string>
#include<string.h>
#include<iterator>
#include<fstream>
#include<map>
#include<unordered_map>
#include<stack>
#include<list>
#include<queue>
#include<iomanip>
#include<bitset>
#pragma GCC optimize(2)
#pragma GCC optimize(3)
#define endl '\n'
#define int ll
#define PI acos(-1)
#define INF 0x3f3f3f3f
typedef long long ll;
typedef unsigned long long ull;
typedef pair<ll, ll>PII;
const int N = 4e5 + 50;
vector<char>graph[300];
unordered_map<char, int>mymap;
void solve()
{
int n;
cin >> n;
string s;
for (int i = 1; i <= n; i++)
{
cin >> s;
graph[s[2]].push_back(s[0]);
graph[s[0]].push_back(s[3]);
if (!mymap.count(s[2]))mymap[s[2]] = 0;
mymap[s[0]]++;
mymap[s[3]]++;
}
queue<char>que;
string str;
for (auto& i : mymap)
{
if (i.second == 0)
{
str += i.first;
que.push(i.first);
}
}
if (que.size() == 0)
{
cout << "No Answer" << endl;
return;
}
while (!que.empty())
{
auto t = que.front();
que.pop();
bool flag = false;
for (auto i : graph[t])
{
mymap[i]--;
if (mymap[i] == 0)
{
str+=i;
que.push(i);
}
}
}
if(mymap.size()!=str.size())
{
cout << "No Answer" << endl;
}
else cout << str << endl;
}
signed main()
{
ios_base::sync_with_stdio(false);
cin.tie(nullptr);
cout.tie(nullptr);
int t = 1;
//cin >> t;
while (t--)
{
solve();
}
return 0;
}