2022-11-19 23:58阅读: 573评论: 5推荐: 2

AtCoder Beginner Contest 278

A - Shift (abc278 a)

题目大意

给定一个有n个整数的数组a,要求进行以下 k次操作,输出操作后的数组。

操作为:将第一个数去掉,在队尾加上一个0

解题思路

模拟即可。

神奇的代码
#include <bits/stdc++.h>
using namespace std;
using LL = long long;
int main(void) {
ios::sync_with_stdio(false);
cin.tie(0); cout.tie(0);
int n, k;
cin >> n >> k;
k = min(n, k);
int x;
for(int i = 0; i < k; ++ i){
cin >> x;
}
for(int i = k; i < n; ++ i){
cin >> x;
cout << x << ' ';
}
for(int i = 0; i < k; ++ i)
cout << 0 << ' ';
return 0;
}


B - Misjudge the Time (abc278 b)

题目大意

定义一个迷惑时间为:交换小时的个位和分钟的十位后,所形成的时间也是有效时间。

给定一个时间h:m,问该时间(包括该时间)之后第一个迷惑时间是多少。

解题思路

时间数就只有24×60=1440,逐一枚举判断即可。

神奇的代码
#include <bits/stdc++.h>
using namespace std;
using LL = long long;
int main(void) {
ios::sync_with_stdio(false);
cin.tie(0); cout.tie(0);
int h, m;
cin >> h >> m;
auto check = [](int h, int m){
int h1 = h / 10;
int h2 = h % 10;
int m1 = m / 10;
int m2 = m % 10;
int H = h1 * 10 + m1;
int M = h2 * 10 + m2;
return H >= 0 && H <= 23 && M >= 0 && M <= 59;
};
auto add = [](int &h, int &m){
++ m;
if (m >= 60){
m -= 60;
h ++;
}
h %= 24;
};
while(true){
if (check(h, m)){
cout << h << ' ' << m << '\n';
break;
}
add(h, m);
}
return 0;
}


C - FF (abc278 c)

题目大意

一个聊天软件,n位用户,有 q次事件,第一个事件为 a关注 b,第二个是取关,第三个问 ab之间是否是互关的关系。

解题思路

因为用户标号达到109,不能直接二维数组,用setmap记录关注信息,直接判断即可。

神奇的代码
#include <bits/stdc++.h>
using namespace std;
using LL = long long;
int main(void) {
ios::sync_with_stdio(false);
cin.tie(0); cout.tie(0);
int n, q;
cin >> n >> q;
set<pair<int, int>> qwq;
while(q--){
int t, a, b;
cin >> t >> a >> b;
if (t == 1){
qwq.insert({a, b});
}else if (t == 2){
qwq.erase({a, b});
}else{
if (qwq.find({a, b}) != qwq.end() && qwq.find({b, a}) != qwq.end())
cout << "Yes" << '\n';
else
cout << "No" << '\n';
}
}
return 0;
}


D - All Assign Point Add (abc278 d)

题目大意

给定一个有n个整数的数组a,有 q次操作,分为三类:

  • 给定x,表示将所有数赋值为 x
  • 给定ix,将 ai增加 x
  • 给定i,询问 ai

解题思路

第一个操作相当于规定基准数,第二个操作我们用一个数组add来维护增加的数,也就是相对于基准数的值。

这样对于第三个操作,ai就是两者的加了。

当再一次操作一时,我们把之前进行过操作二的数据add对应的位置重置为0即可。总的时间复杂度就是O(q)(势能分析的角度,每一次操作二会给操作一带来1的势能,总的势能不超过 O(q)

神奇的代码
#include <bits/stdc++.h>
using namespace std;
using LL = long long;
int main(void) {
ios::sync_with_stdio(false);
cin.tie(0); cout.tie(0);
int n;
cin >> n;
vector<LL> a(n);
set<int> id;
for(int i = 0; i < n; ++ i)
id.insert(i);
LL base = 0;
for(auto &i : a)
cin >> i;
int q;
cin >> q;
while(q--){
int op;
cin >> op;
if (op == 1){
for(auto &i : id){
a[i] = 0;
}
id.clear();
cin >> base;
}else if (op == 2){
int pos;
LL val;
cin >> pos >> val;
-- pos;
a[pos] += val;
id.insert(pos);
}else {
int pos;
cin >> pos;
-- pos;
cout << base + a[pos] << '\n';
}
}
return 0;
}


E - Grid Filling (abc278 e)

题目大意

给定一个矩形,格子上有数。

给定h,w,意味大小为h×w的黑布,该黑布从矩形左上角向右移动,向下移动,直到右下角。问每个位置,将黑布的数遮盖后,剩下未被遮盖的不同的数的个数。

解题思路

暴力即可,黑布往右移动的时候,加上被遮住的一列,减去被遮住的一列。总的时间复杂度为O(n3)

神奇的代码
#include <bits/stdc++.h>
using namespace std;
using LL = long long;
int main(void) {
ios::sync_with_stdio(false);
cin.tie(0); cout.tie(0);
int H, W, N, h, w;
cin >> H >> W >> N >> h >> w;
vector<vector<int>> a(H, vector<int>(W));
vector<int> cnt(N + 1, 0);
int ans = 0;
for(auto &i : a)
for(auto &j : i){
cin >> j;
cnt[j] ++;
if (cnt[j] == 1)
++ ans;
}
vector<int> tmp(cnt.begin(), cnt.end());
int bac = ans;
for(int i = 0; i <= H - h; ++ i){
for(int j = i; j < i + h; ++ j)
for(int k = 0; k < 0 + w; ++ k){
cnt[a[j][k]] --;
if (cnt[a[j][k]] == 0)
-- ans;
}
cout << ans;
for(int j = 1; j <= W - w; ++ j){
for(int k = i; k < i + h; ++ k){
cnt[a[k][j - 1]] ++;
if (cnt[a[k][j - 1]] == 1)
++ ans;
}
for(int k = i; k < i + h; ++ k){
cnt[a[k][j + w - 1]] --;
if (cnt[a[k][j + w - 1]] == 0)
-- ans;
}
cout << ' ' << ans;
}
cout << '\n';
ans = bac;
cnt = tmp;
}
return 0;
}

也可以记录一个二维前缀和cnt[i][j][k]表示 (1,1)(i,j)的矩形中,数字 k出现的次数。然后对于每次询问通过前缀和得到剩下各个数字出现的次数,再检查非0的个数。复杂度也是 O(n3),且这种更好写一点。


F - Shiritori (abc278 f)

题目大意

给定n个字符串sab在玩。 a先手。

每个人说出一个1~n数字i,该数字必须是之前未说过的,且si的首字母和sj的尾字母相同,其中 j是上一个人说过的数字。

问两者都是绝顶聪明的情况下,谁赢。

解题思路

dp[s][i]表示目前已经选择的数字状态为 s,且最后说过的数字是 i,当前状态是必胜还是必输,枚举后继状态,直接搜索即可。

神奇的代码
#include <bits/stdc++.h>
using namespace std;
using LL = long long;
int main(void) {
ios::sync_with_stdio(false);
cin.tie(0); cout.tie(0);
int n;
cin >> n;
vector<vector<int>> edge(n);
vector<pair<int, int>> qwq;
for(int i = 1; i <= n; ++ i){
string s;
cin >> s;
qwq.push_back({s.front(), s.back()});
}
for(int i = 0; i < n; ++ i)
for(int j = 0; j < n; ++ j){
if (i == j)
continue;
if (qwq[i].second == qwq[j].first)
edge[i].push_back(j);
}
vector<vector<int>> dp((1 << n), vector<int>(n, -1));
function<int(int, int)> dfs = [&](int s, int la){
if (dp[s][la] != -1)
return dp[s][la];
int ok = 1;
for(auto nxt : edge[la]){
if ((s >> nxt) & 1)
continue;
ok &= dfs(s | (1 << nxt), nxt);
}
return dp[s][la] = (ok ^ 1);
};
int ok = 1;
for(int i = 0; i < n; ++ i){
ok &= dfs((1 << i), i);
}
if (!ok)
cout << "First" << '\n';
else
cout << "Second" << '\n';
return 0;
}


G - Generalized Subtraction Game (abc278 g)

题目大意

有写有数字1~n的牌放在桌子上,给定l,r,你和一个绝顶聪明的人玩一个游戏。你可以选择你先手还是后手,然后轮流进行的操作是:

  • 选择一个连续区间,其长度在[l,r]内。然后将桌子上该区间的数字牌全部拿走。注意必须区间上的所有数字牌都在桌子上。

谁无法操作就输。

请写一个程序,打赢这个绝顶聪明的人。

解题思路

蛮有意思的交互题,是一场真·博弈对决。

第一次操作相当于将一个连续的区间一分为二,可以考虑一种经典策略:对称操作。如果将区间分成两个一模一样,那么对方对区间a做的操作,我都可以对区间 b做同样的操作。这样一定是对方最先变得无法操作。

[1,n]拆成 [1,x],[x+1,nx],[nx+1,n] ,其中中间那个是丢掉的,很显然中间的区间数量的奇偶性和n相同(前后两个区间内数量加起来必是偶数)。因此当 l==rln奇偶性相等 时,或者l<r时(此时 ll+1必定有个与 n奇偶性相同)都可以采用此策略:选择先手,将区间拆成两个相等的,然后模仿对方行为做即可。

但当 l==rln的奇偶性不相等时,此时我们考虑暴力了。

该题本质上还是博弈题。当我们对一个区间一分为二后,新出来的两个区间本质上就是两个独立的游戏,因此此时游戏的sg值就是这两个区间的 sg值的异或。

因此我们求得所有长度的sg值,先根据初态是必胜态还是必败态选择先后手,然后枚举选择一种操作使得下一个局面是必败态(对方必败)。

预处理sg值的复杂度是 O(n2),即枚举当前长度,然后枚举操作起点(操作长度是固定的为 l)。

后来枚举下一步操作的复杂度是 O(n)

事实上 lr的话,两者的复杂度都要乘以 n。不过有人说可以在O(n2logn)的时间内预处理,每次操作的复杂度是O(nlogn)

神奇的代码
#include <bits/stdc++.h>
using namespace std;
using LL = long long;
int main(void) {
ios::sync_with_stdio(false);
cin.tie(0); cout.tie(0);
int n, l, r;
cin >> n >> l >> r;
if (r - l >= 1 || ((l & 1) == (n & 1))){
int op = l + ((l & 1) != (n & 1));
int left = (n - op) >> 1;
cout << "First" << endl;
cout << left + 1 << ' ' << op << endl;
int x, y;
while(true){
cin >> x >> y;
if (x == 0 || x == -1)
break;
if (x <= left){
cout << x + left + op << ' ' << y << endl;
}else {
cout << x - left - op << ' ' << y << endl;
}
}
}else{
vector<int> sg(n + 1, 0);
vector<int> cnt(n + 5, 0);
for(int i = 1; i <= n; ++ i){
for(int j = 1; j <= i; ++ j){
if (j + l - 1 > i)
break;
cnt[sg[j - 1] ^ sg[i - (j + l - 1)]] = 1;
}
int val = 0;
while(cnt[val])
++ val;
sg[i] = val;
fill(cnt.begin(), cnt.end(), 0);
}
set<pair<int, int>> s;
s.insert({1, n});
int cur = sg[n];
auto work = [&](){
for(auto &c : s){
for(int i = c.first; i <= c.second; ++ i){
if (i + l - 1 > c.second)
break;
int nxt = cur ^ sg[c.second - c.first + 1] ^ sg[i - c.first] ^ sg[c.second - (i + l - 1)];
if (!nxt)
return i;
}
}
return 0;
};
auto remove = [&](int pos){
for(auto &c : s){
if (pos >= c.first && pos <= c.second){
cur ^= sg[c.second - c.first + 1] ^ sg[pos - c.first] ^ sg[c.second - (pos + l - 1)];
s.insert({c.first, pos - 1});
s.insert({pos + l, c.second});
s.erase(c);
return;
}
}
};
if (sg[n]){
cout << "First" << endl;
int pos = work();
cout << pos << ' ' << l << endl;
remove(pos);
}else{
cout << "Second" << endl;
}
while(true){
int x, y;
cin >> x >> y;
if (x == 0 || x == -1)
break;
remove(x);
int pos = work();
cout << pos << ' ' << l << endl;
remove(pos);
}
}
return 0;
}


Ex - make 1 (abc278 h)

题目大意

<++>

解题思路

<++>

神奇的代码


本文作者:~Lanly~

本文链接:https://www.cnblogs.com/Lanly/p/16907585.html

版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。

posted @   ~Lanly~  阅读(573)  评论(5编辑  收藏  举报
点击右上角即可分享
微信分享提示
💬
评论
📌
收藏
💗
关注
👍
推荐
🚀
回顶
收起
  1. 1 404 not found REOL
404 not found - REOL
00:00 / 00:00
An audio error has occurred.