2023-03-17 19:59阅读: 303评论: 0推荐: 0

AtCoder Beginner Contest 293

上周因为GDKOI咕咕咕了

A - Swap Odd and Even (abc293 a)

题目大意

给定一个字符串,交换每两个相邻字母,输出结果。

解题思路

模拟即可。

神奇的代码
#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);
string s;
cin >> s;
for(int i = 0; i < s.size(); i += 2)
swap(s[i], s[i + 1]);
cout << s << '\n';
return 0;
}


B - Call the ID Number (abc293 b)

题目大意

每人有一个标号和一个叫号,第i个人标号为 i,叫号为 ai

依次对每个人,如果此人的标号没被叫到,则此人会叫其叫号,否则直接到下一个人。

问最终有多少人不会叫。

解题思路

按照题意模拟即可。

其实题意都没看懂,看了样例才明白。

神奇的代码
#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<int> call(n, 0);
for(int i = 0; i < n; ++ i){
int x;
cin >> x;
-- x;
if (call[i] == 0)
call[x] = 1;
}
int ans = count(call.begin(), call.end(), 0);
cout << ans << '\n';
for(int i = 0; i < n; ++ i){
if (call[i] == 0)
cout << i + 1 << ' ';
}
cout << '\n';
return 0;
}


C - Make Takahashi Happy (abc293 c)

题目大意

二维网格,左上走到右下,一次往右或往下走一格。格子上有数字。

问有多少种路径,其走过的数据互不相同。

解题思路

网格只有10×10,暴力复杂度是 O(220),直接搜即可。

神奇的代码
#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;
cin >> h >> w;
vector<vector<int>> g(h, vector<int>(w, 0));
for(auto &i : g)
for(auto &j : i)
cin >> j;
int ans = 0;
set<int> qwq;
function<void(int, int)> dfs = [&](int x, int y){
if (x == h - 1 && y == w - 1){
++ ans;
return;
}
if (x != h - 1 && qwq.count(g[x + 1][y]) == 0){
qwq.insert(g[x + 1][y]);
dfs(x + 1, y);
qwq.erase(g[x + 1][y]);
}
if (y != w - 1 && qwq.count(g[x][y + 1]) == 0){
qwq.insert(g[x][y + 1]);
dfs(x, y + 1);
qwq.erase(g[x][y + 1]);
}
};
qwq.insert(g[0][0]);
dfs(0, 0);
cout << ans << '\n';
return 0;
}


D - Tying Rope (abc293 d)

题目大意

n条绳子, m个捆绑操作。

每个操作将两个绳子的一端绑起来。一端最多只能被捆绑一次。

问最终得到了多少个环形绳子和非环形绳子。

解题思路

将每个绳子看成一个点,绑起来相当于连一条边。

绳子只有两端意味着每个点至多只有两条边与其相连。

因此每条边,要么是环上的边,要么是链上的边,不会是环里横跨环的边。

即最终的图只有两种:x个点 x条边的环,以及 x个点 x1条边的链。

因此我们就统计一下最终图的连通块的数量,做做容斥就出来了。(考虑一条边减少一个连通块)

而统计连通块的数量则用并查集。

神奇的代码
#include <bits/stdc++.h>
using namespace std;
using LL = long long;
class dsu {
public:
vector<int> p;
vector<int> sz;
int n;
dsu(int _n) : n(_n) {
p.resize(n);
sz.resize(n);
iota(p.begin(), p.end(), 0);
fill(sz.begin(), sz.end(), 1);
}
inline int get(int x) {
return (x == p[x] ? x : (p[x] = get(p[x])));
}
inline bool unite(int x, int y) {
x = get(x);
y = get(y);
if (x != y) {
p[x] = y;
sz[y] += sz[x];
return true;
}
return false;
}
};
int main(void) {
ios::sync_with_stdio(false);
cin.tie(0); cout.tie(0);
int n, m;
cin >> n >> m;
dsu d(n);
for(int i = 0; i < m; ++ i){
int x, y;
string s;
cin >> x >> s >> y >> s;
-- x;
-- y;
d.unite(x, y);
}
int cnt = 0;
for(int i = 0; i < n; ++ i)
cnt += (d.get(i) == i);
int ans1 = m - (n - cnt), ans2 = cnt - ans1;
cout << ans1 << ' ' << ans2 << '\n';
return 0;
}


E - Geometric Progression (abc293 e)

题目大意

给定a,x,m,求 i=0x1aimodm

解题思路

因为递推式和求和式都是线性的,因此可以用矩阵来求。

[aisi1]=[a011][ai1si2]

因此就中间那个矩阵的x次幂 ×初始矩阵状态就能得到其和 sn了。

初始矩阵就是

[10]

x次幂则用快速幂的形式求即可。

如果用等比数列的话需要求a1m下的逆元,而这必须得保证 a1m互质,题意没保证,故无法求。

神奇的代码
#include <bits/stdc++.h>
using namespace std;
using LL = long long;
#define FOR(i, x, y) for (decay<decltype(y)>::type i = (x), _##i = (y); i < _##i; ++i)
#define FORD(i, x, y) for (decay<decltype(x)>::type i = (x), _##i = (y); i > _##i; --i)
LL MOD;
struct Mat {
static const LL M = 2;
LL v[M][M];
Mat() { memset(v, 0, sizeof v); }
void eye() { FOR (i, 0, M) v[i][i] = 1; }
LL* operator [] (LL x) { return v[x]; }
const LL* operator [] (LL x) const { return v[x]; }
Mat operator * (const Mat& B) {
const Mat& A = *this;
Mat ret;
FOR (k, 0, M)
FOR (i, 0, M) if (A[i][k])
FOR (j, 0, M)
ret[i][j] = (ret[i][j] + A[i][k] * B[k][j]) % MOD;
return ret;
}
Mat pow(LL n) const {
Mat A = *this, ret; ret.eye();
for (; n; n >>= 1, A = A * A)
if (n & 1) ret = ret * A;
return ret;
}
Mat operator + (const Mat& B) {
const Mat& A = *this;
Mat ret;
FOR (i, 0, M)
FOR (j, 0, M)
ret[i][j] = (A[i][j] + B[i][j]) % MOD;
return ret;
}
void prt() const {
FOR (i, 0, M)
FOR (j, 0, M)
printf("%lld%c", (*this)[i][j], j == M - 1 ? '\n' : ' ');
}
};
int main(void) {
ios::sync_with_stdio(false);
cin.tie(0); cout.tie(0);
LL a, x, m;
cin >> a >> x >> m;
MOD = m;
Mat qwq;
qwq[0][0] = a;
qwq[1][0] = qwq[1][1] = 1;
Mat ans = qwq.pow(x);
LL res = ans[1][0];
cout << res << '\n';
return 0;
}


F - Zero or One (abc293 f)

题目大意

给定一个数字x,问多少个 b,使得 xb进制下,每个数位要么是 0要么是 1

多组询问。

解题思路

随着b增大, xb进制下的数位的数量是越来越小的。

我们可以暴力判断小的进制是否符合要求,对于大的进制,因为位数很少,可以花 2n枚举每个数位的状态(是0还是1),再二分一下进制b,看看是否存在一个进制满足上述要求。

神奇的代码
#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 t;
cin >> t;
while(t--){
LL x;
cin >> x;
int up = min(5000ll, x);
int ans = 1;
auto check1 = [&](int base){
LL tmp = x;
while(tmp){
if ((tmp % base) > 1)
return false;
tmp /= base;
}
return true;
};
for(int i = 3; i <= up; ++ i){
ans += check1(i);
}
int up2 = log(x) / log(up) + 1;
auto check2 = [&](int s){
__int128 l = up + 1, r = x + 1;
auto solve = [&](__int128 val){
__int128 tmp = 1, sum = 0;
int tmps = s;
for(int i = 0; i < up2; ++ i){
sum += tmp * (tmps & 1);
tmp *= val;
tmps >>= 1;
}
return sum;
};
while(l + 1 < r){
__int128 mid = (l + r) >> 1;
__int128 sum = solve(mid);
if (sum >= 0 && sum <= x) // sum < 0 => overflow => sum > x
l = mid;
else
r = mid;
}
return solve(l) == x;
};
for(int i = 0; i < (1 << up2); ++ i){
ans += check2(i);
}
cout << ans << '\n';
}
return 0;
}


G - Triple Index (abc293 g)

题目大意

给定一个n个数字的数组a,和 q组询问,每组询问给定 l,r,问有多少个 li<j<kr,满足 ai=aj=ak

解题思路

考虑一个询问,其答案就是(cnt[i]3),其中 cnt[i]表示这个该询问区间中数字 i的数量。

注意到当询问区间发生变化时, cnt数组非常容易维护,且每次移位只有一个 cnt值发生变化。因此离线莫队即可。

神奇的代码
#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;
vector<int> a(n);
for(auto &i : a)
cin >> i;
vector<LL> cnt(200001, 0);
vector<array<int, 2>> query(q);
for(auto &i : query){
cin >> i[0] >> i[1];
i[0] --;
}
vector<int> id(q, 0);
int blk_num = sqrt(q), blk_size = q / blk_num;
vector<int> belong(n);
for(int i = 0; i < blk_num; ++ i){
for(int j = i * blk_size; j < (i + 1) * blk_size && j < n; ++ j)
belong[j] = i;
}
iota(id.begin(), id.end(), 0);
sort(id.begin(), id.end(), [&](int a, int b){
if (belong[query[a][0]] != belong[query[b][0]]){
return belong[query[a][0]] < belong[query[b][0]];
}else {
return bool((query[a][1] < query[b][1]) ^ (belong[query[a][0]] & 1));
}
});
int l = 0, r = 0;
vector<LL> ans(q);
LL cur = 0;
auto mv = [&](int pos, int val){
int num = a[pos];
if (cnt[num] >= 3)
cur -= cnt[num] * (cnt[num] - 1) * (cnt[num] - 2) / 6;
cnt[num] += val;
if (cnt[num] >= 3)
cur += cnt[num] * (cnt[num] - 1) * (cnt[num] - 2) / 6;
};
for(auto &i : id){
int L = query[i][0], R = query[i][1];
debug(L, R);
while(l > L)
mv(-- l, 1);
while(r < R)
mv(r ++, 1);
while(l < L)
mv(l ++, -1);
while(r > R)
mv(-- r, -1);
ans[i] = cur;
}
for(auto &i : ans)
cout << i << '\n';
return 0;
}


Ex - Optimal Path Decomposition (abc293 h)

题目大意

给定一棵树,要求对每个节点涂色,最小化数字k,使得:

  • 每个颜色的节点构成一个连通块
  • 任意一条简单路径上,节点的颜色种类不超过k

求该 k

解题思路

<++>

神奇的代码


本文作者:~Lanly~

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

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

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