A.Tokitsukaze and Bracelet
签到题,直接上代码。
#include<bits/stdc++.h>
using namespace std;
int main()
{
ios::sync_with_stdio(0);
cin.tie(0);
int T; cin >> T;
while (T--){
int a, b, c;
cin >> a >> b >> c;
int ans = 0;
ans += (a - 100) / 50;
if (b > 32 && b <= 40) ans += 1;
else if (b == 45) ans += 2;
if (c > 32 && c <= 40) ans++;
else if (c == 45) ans += 2;
cout << ans << '\n';
}
return 0;
}
B.Tokitsukaze and Cats
围住每个猫咪需要被四块防猫网围住,但是当某个格子的相邻位置存在猫咪时,两个格子存在公共边,只需要放一块防猫网。所以只需要判断当前猫咪的相邻位置上是否存在猫咪即可,如果不存在则需要放置一块新的防猫网。
#include<bits/stdc++.h>
using namespace std;
typedef pair<int, int> PII;
map<PII, bool> st;
int main()
{
ios::sync_with_stdio(0);
cin.tie(0);
int n, m, k;
cin >> n >> m >> k;
int ans = 0;
for (int i = 0; i < k; i++){
int x, y;
cin >> x >> y;
if (st[{x - 1, y}] == false) ans++;
if (st[{x + 1, y}] == false) ans++;
if (st[{x, y - 1}] == false) ans++;
if (st[{x, y + 1}] == false) ans++;
st[{x, y}] = true;
}
cout << ans;
return 0;
}
E.Tokitsukaze and Eliminate (easy)
easy版本只有1和2两种颜色。我们可以先把1和2出现的位置保存下来,因为每次要将颜色为x的最右边那颗宝石、以及该宝石右边的所有宝石全部消除,要保证最少的操作次数,所以需要判断1和2的最右边的下标中哪个更小,删除小的那个,然后将1和2中在这个数后面的位置全部去掉。例如用例中第三组数据中的删除顺序是1,1,2,1,2,2。
#include<bits/stdc++.h>
using namespace std;
int main()
{
ios::sync_with_stdio(0);
cin.tie(0);
int T;
cin >> T;
while (T--){
int n; cin >> n;
vector<int> a(n);
for (int i = 0; i < n; i++) cin >> a[i];
map<int, vector<int>> mp;
for (int i = 0; i < n; i++) {
mp[a[i]].push_back(i);
}
int ans = 0;
while (mp[1].size() || mp[2].size()){
int t;
//去1和2中最右端的位置的最小值
if (mp[1].size() && mp[2].size()) t = min(mp[1].back(), mp[2].back());
else {
if (mp[1].size()) t = mp[1].back();
else t = mp[2].back();
}
ans++;
//将位于t后面的数都删除
while (mp[1].size() && mp[1].back() >= t) mp[1].pop_back();
while (mp[2].size() && mp[2].back() >= t) mp[2].pop_back();
}
cout << ans << '\n';
}
return 0;
}
F.Tokitsukaze and Eliminate (hard)
hard版本颜色数量变多,但是基本思路不变。easy版本中我们只需要考虑1和2两种颜色的最右端的最小值,hard版本则需要判断所有颜色的最右端的最小值。我们可以使用map来实现,因为我们每个数最多被删除一次,所以不考虑map的时间复杂度时的时间复杂度是,显然考虑map的时间复杂度也是能过的。但是需要注意,要是map中某个数字的下标完全被删完了,要从map中删掉这个key,防止下一次还访问到这个数,真正做到
,不然就会和我一样一直T。
#include<bits/stdc++.h>
using namespace std;
int main()
{
ios::sync_with_stdio(0);
cin.tie(0);
int T;
cin >> T;
while (T--){
int n; cin >> n;
vector<int> a(n);
for (int i = 0; i < n; i++) cin >> a[i];
map<int, vector<int>> mp;
for (int i = 0; i < n; i++) {
mp[a[i]].push_back(i);
}
int ans = 0;
int t = n;
while (t >= 0){
int res = t;
vector<int> c;
//遍历每一中颜色
for (auto &[x, y] : mp){ //一定要使用&带修,不然也会WA
while (y.size() && y.back() > t) y.pop_back(); //将上一次操作后应该删除的数字删除
if (y.size()) res = min(res, y.back()); //取所有颜色的最右端中最小的一个
else c.push_back(x); //某个数字不再出现在剩下的数组中
}
//从map中删除已经删完的数字
for (auto x : c){
mp.erase(x);
}
t = res - 1;
ans++;
}
cout << ans << '\n';
}
return 0;
}
I.Tokitsukaze and Short Path (plus)
将边权公式展开可以发现两点之间的边权值为两个顶点中的最大值的两倍。走法有两个:直接走两点间的边;或者是通过其他点然后到达目标点。因为边权都是两个顶点中最大值的两倍,所以两点间的最短路一定是直接走两点间的边。结合前面的分析可以得出,顶点的值越大,被使用的次数越多,顶点值最大的会被所有点使用,顶点值第二大的只是不会被顶点值最大的点使用。所以每个顶点值被使用的次数取决于他在所有顶点中的大小。
假设有4个点,排序后的顶点值分别为,所以当所求式子的
时,
。剩下的以此类推,可以得到
。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 2e5 + 6, INF = 0x3f3f3f3f;
int main()
{
ios::sync_with_stdio(0);
cin.tie(0);
int T; cin >> T;
while (T--){
ll n;
cin >> n;
vector<ll> a(n + 5);
for (int i = 1; i <= n; i++) cin >> a[i];
if (n == 1){
cout << 0 << '\n';
continue;
}
sort(a.begin() + 1, a.begin() + 1 + n);
ll ans = 0;
for (int i = 1; i <= n; i++){
ans += (i - 1) * a[i] * 4LL;
}
cout << ans << '\n';
}
return 0;
}
J.Tokitsukaze and Short Path (minus)
将边权公式展开可以发现两点之间的边权值为两个顶点中的最小值的两倍。这个题与I题的区别在于两点间的最短路是,即从
点先走到顶点值最小的点再走去
点,或者是直接从
走到
,取两者的最小值。这个题和I题相反,顶点值越小被使用次数最多,和I题相同的方法可以推出
。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 2e5 + 6, INF = 0x3f3f3f3f;
int main()
{
ios::sync_with_stdio(0);
cin.tie(0);
int T; cin >> T;
while (T--){
ll n;
cin >> n;
vector<ll> a(n + 5);
for (int i = 1; i <= n; i++) cin >> a[i];
if (n == 1){
cout << 0 << '\n';
continue;
}
sort(a.begin() + 1, a.begin() + 1 + n);
ll ans = 0;
for (int i = 1; i <= n; i++){
ans += min(a[1] * 4LL, a[i] * 2) * (n - i) * 2;
}
cout << ans << '\n';
}
return 0;
}
K.Tokitsukaze and Password (easy)
看题目数据范围,直接暴力枚举就行。根据题目的情报1,2,3,可以写出check函数判断枚举出的数字是否合法。然后情报4规定了未知内容,未知的内容就是所给字符串中的a,b,c,d和_,所以我们直接枚举a,b,c,d,_的值即可。但是需要注意a,b,c,d的值互不相同。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
ll n, y;
int a[10];
bool check(int x){
if (x > y || x % 8) return false; //不满足情报2或情报3
if (x == 0 && n == 1) return true; //特判0;只有n=1时,x=0合法
for (int i = 1; i < n; i++) x /= 10;
if (!x) return false; //存在前导0
else return true;
}
int main()
{
ios::sync_with_stdio(0);
cin.tie(0);
int T; cin >> T;
while (T--){
map<int, int> mp;
cin >> n;
string s; cin >> s;
cin >> y;
int ans = 0;
for (a[0] = 0; a[0] <= 9; a[0]++) //枚举a的值
{
for (a[1] = 0; a[1] <= 9; a[1]++) //枚举b的值
{
if (a[0] == a[1]) continue; //保证不同
for (a[2] = 0; a[2] <= 9; a[2]++) //枚举c的值
{
if (a[2] == a[0] || a[2] == a[1]) continue; //保证不同
for (a[3] = 0; a[3] <= 9; a[3]++) //枚举d的值
{
if (a[3] == a[0] || a[3] == a[1] || a[3] == a[2]) continue; //保证不同
for (int _ = 0; _ <= 9; _++) //枚举_的值
{
int res = 0;
for (auto c : s){
if (c >= '0' && c <= '9'){
res = res * 10 + (c - '0');
} else if (c >= 'a' && c <= 'd'){
res = res * 10 + a[c - 'a'];
} else {
res = res * 10 + _;
}
}
if (mp[res]) continue; //保证没有枚举到相同的数字
mp[res]++;
if (check(res)) ans++;
}
}
}
}
}
cout << ans << '\n';
}
return 0;
}