AtCoder Beginner Contest 262 A-F
AtCoder Beginner Contest 262 A-F
https://atcoder.jp/contests/abc262
我太懒了,现在才发
A - World Cup
题意
给定y,找到>=y的离y最近的模4等于2的数
分析
如题
Code
#include <bits/stdc++.h>
using namespace std;
int main () {
int x;
cin >> x;
int mod = x % 4;
if (mod == 0) x += 2;
else if (mod == 1) x ++;
else if (mod == 3) x += 3;
cout << x;
}
B - Triangle (Easier)
题意
给定无向图,找有多少对点a,b,c,满足:
a和b之间有边,a和c之间有边,c和b之间有边
分析
n很小,可以直接三重循环枚举
Code
#include <bits/stdc++.h>
using namespace std;
const int N = 105;
bool f[N][N];
int main () {
int n, m, ans = 0;
cin >> n >> m;
while (m --) {
int x, y;
cin >> x >> y;
f[x][y] = f[y][x] = true;
}
for (int i = 1; i <= n; i ++)
for (int j = i + 1; j <= n; j ++)
for (int k = j + 1; k <= n; k ++)
if (f[i][j] && f[j][k] && f[k][i]) ans ++;
cout << ans << endl;
}
C - Min Max Pair
题意
给定长度为n的序列a,问有多少对i,j满足:
分析
要满足上述情况,要么:
- \(a_i=i,a_j=j\)
- \(a_i=j,a_j=i\)
对于情况2,找出所有满足的点对即可
对于情况1,统计\(a_i=i\)的数量x,然后\(\frac{x*(x-1)}{2}\)计入贡献
Code
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N = 5e5 + 5;
int a[N];
bool vis[N];
signed main () {
int n;
cin >> n;
for (int i = 1; i <= n; i ++) cin >> a[i];
int pure = 0, ans = 0;
for (int i = 1; i <= n; i ++) {
if (vis[i]) continue;
if (a[i] == i) pure ++;
else {
if (a[a[i]] == i) {
ans ++;
vis[a[i]] = true;
}
}
}
ans += (pure-1)*pure/2;
cout << ans;
}
//没开ll,卧槽
D - I Hate Non-integer Number
题意
长度为n的序列a中选1-n个数,问这些数之和除以所选数量为整数的组合有多少种
分析
组合有很多可能,且存在"可转移"的特点,考虑dp
dp[j][k][l]: 分母为i, 前j个数中选了k个数,之和的余数为l的方案数
转移:
- 不选:dp[j+1][k][l] += dp[j][k][l];
- 选:dp[j+1][k+1][(l+a[j])%i] += dp[j][k][l]; (j < i)
Code
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N = 105, mod = 998244353;
int a[N], f[N][N][N], n, ans;
signed main () {
cin >> n;
for (int i = 0; i < n; i ++) cin >> a[i];
for (int i = 1; i <= n; i ++) {
memset (f, 0, sizeof f);
f[0][0][0] = 1;
for (int j = 0; j < n; j ++)
for (int k = 0; k <= i; k ++)
for (int l = 0; l < i; l ++) {
f[j+1][k][l] = (f[j+1][k][l] + f[j][k][l]) % mod; //不取
if(k != i) f[j+1][k+1][(l+a[j])%i] = (f[j+1][k+1][(l+a[j])%i] + f[j][k][l]) % mod; //取
}
ans = (ans + f[n][i][0]) % mod;
}
cout << ans << endl;
}
//是一个很难想的dp式子(是我见得少了)
//积累套路
//f[j][k][l]:当前分母为i时,前j个数选了k个时,余数为l的方案数
//不取: f[j+1][k][l] += f[j][k][l];
//取: f[j+1][k+1][(l+a[j])%i] += f[j][k][l];
//注意取得前提条件一定是 k!=i
E - Red and Blue Graph
题意
给定无向图,每个点都可染成红色或蓝色,求在满足:
- 有k个点被染成红色
- 两端点颜色不一样的边的个数为偶数
的条件下,可能的方案数
分析
要尽可能与限制条件挂上关系,考虑设:
- 设两端点都是红点的边有 b 个,一红一蓝的边有 c 个
- 所有红色点的度之和为 s
则 \(s = 2*b+c\)
故 所有红色点的度之和为偶数
找偶数个奇数点,组合数求解
原因:
假设选 \(i\) 个度数为奇数的点奇,因为奇数偶数=偶数,为了使得最终的度数之和是偶数,这里的 \(i\) 一定要是偶数,也就是选了偶数个奇数点,那么剩下 \(k-i\) 个点都是度数为偶数的点了(偶数任何数=偶数),一定满足度数和为偶数的条件。
Code
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 2e5 + 5, mod = 998244353;
int d[N];
int n, m, k, odd;
ll ans;
int fact[N], infact[N];
int qmi(int a, int k, int p){
int res = 1;
while(k){
if(k & 1)
res = (ll)res * a % p;
a = (ll)a * a % p;
k >>= 1;
}
return res;
}
void init () {
fact[0] = infact[0] = 1;
for(int i = 1; i < N; i++){
fact[i] = (ll)fact[i-1] * i % mod;
infact[i] = (ll)infact[i-1] * qmi(i, mod - 2,mod) % mod;
}
}
ll C (int a, int b) {
return (ll)fact[a] * infact[b] % mod * infact[a - b] % mod;
}
void solve () {
init ();
cin >> n >> m >> k;
while (m --) {
int x, y;
cin >> x >> y;
d[x] ++, d[y] ++;
}
for (int i = 1; i <= n; i ++) {
if (d[i] & 1) odd ++;
}
//奇数个偶数
for (int i = 0; i <= k; i += 2) {
if (i > odd || k - i > n - odd) continue;
ans = (ans + C (odd, i) * C (n-odd, k-i) % mod) % mod;
}
cout << ans << endl;
}
int main () {
solve ();
}
// 一共有 k 个点被染成红色
// 一共有偶数条边, 连接不同的颜色
// red点度数之和为s,边的两点都是red有b个,颜色不同的有c个
// 有: s = 2 * b + c
// 故 s 为偶数
// 寻找k个点,使他们的点的度数之和为偶数
// 找偶数个奇数点
F - Erase and Rotate
题意
可以对长度为n的序列p进行如下操作0-k次:
- 选择任一pi,删去他
- 把最末尾的pi挪到最前面去
问最后得到的字典序最小的序列是啥
分析
如果操作合适的话,每次操作必然会使得字典序减小,故一定会操作k次
考虑两种方案:
先找到最小的数字所在的位置pos
-
纯删除(pos <= k):
那么在pos之前的字符全部要删掉,然后剩余可操作的可利用单调栈来维护一个p[pos]打头,字典序最小的序列
还有剩余的就从最后开始往前删 -
删除+旋转 (n-pos<=k)
对于一个pi,删除+旋转/旋转+删除等价于直接删了他,所以任一pi要么删除,要么旋转。
等价于先把pos之后的全删了(因为比p[pos]大,转到前面去也不会更优),p[pos]转到第一个
后续的操作就和1.的后半部分一样了,单调栈+剩余尾删
Code
#include <bits/stdc++.h>
using namespace std;
const int N = 2e5 + 5;
int p[N], n, k, pos = -1;
vector<int> change1 () {
vector <int> v1{n+1}; //默认该策略不可行
//只删除
if (pos <= k) {
v1.clear();
int res = k - pos;
for (int i = pos; i < n; i++) {
while (!v1.empty() && p[i] < v1.back() && res > 0) {
res--;
v1.pop_back();
}
v1.push_back(p[i]);
}
//还有剩余的次数删末尾的
while (res > 0) {
v1.pop_back();
res--;
}
}
return v1;
}
vector <int> change2 () {
vector <int> v2{n+1};
//旋转+删除
if (n - pos <= k) {
v2.clear();
int res = k - (n - pos); //删除之后还剩多少次
for (int i0 = 0; i0 < n; i0++) {
int i = (i0 + pos) % n;
while (!v2.empty() && p[i] < p[v2.back()] && res >= (v2.back() < pos)) {
res -= (v2.back() < pos); //在删除点之前的,没旋转过,执行删除操作;在此之后的转了之后再删等价于直接删,故只减1次
v2.pop_back();
}
v2.push_back(i);
}
//剩余尾删
while (res >= (v2.back() < pos)) {
res -= (v2.back() < pos);
v2.pop_back();
}
for (auto &i : v2) {
i = p[i];
}
}
return v2;
}
int main() {
cin >> n >> k;
for (int i = 0; i < n; i++) cin >> p[i];
//找前k个和后k个范围内的最小值
for (int i = 0; i < n; i ++) {
if (i <= k || n - i <= k) {
if (pos == -1 || p[i] < p[pos]) {
pos = i;
}
}
}
auto ans = min(change1(), change2());
n = ans.size();
for (int i = 0; i < n; i++)
cout << ans[i] << ' ';
cout << endl;
}
//操作:
//1. 删掉一个pi
//2. 把最末尾的pi挪到队头
//策略:
//1. 只删除
//2. 旋转+删除:先转完再删
很有意思