今天凌晨 CF D 题差一句判断 AC,晚上 ABC G 题把插板法和快速幂搞混差点 AC。
事不过三,再这样一次我直接紫砂。
太简单的题就不放翻译和代码了。(事实上这场 A-E 都是大水题。。)
Ex 太难了先润了,以后再说吧。
A - Rightmost
模拟即可。
B - Adjacency List
std::set
模拟即可。
C - Previous Permutation
给定一个 \(1\sim n\) 的排列 \(p\),已知 \(p\) 是 \(1\sim n\) 的所有排列中字典序排名第 \(k\) 小的,求字典序第 \(k-1\) 小的排列。
\(1\le n\le 100\)
看到题面,非常熟悉,直接进行一个康托展开的写(
因为 \(n\le 100\),所以并不用康托展开。
upd:艹,我 sb 了,直接 std::prev_permutation
即可,下面的东西不用看了 qwq。
设我们要求的排列为 \(p'\)。
我们发现,一定存在一个位置 \(j\),使得 \(p_{j+1}\lt p_{j+2}\lt\dots\lt p_n\) 且 \(p'_{j+1}\gt p'_{j+2}\gt\dots\gt p'_n\)
从 \(1\) 到 \(n\) 枚举 \(j\),若满足条件,将 \(p_{j+1}\sim p_{n}\) 中 \(p_j\) 的前驱与 \(p_j\) 交换,然后降序排序即可。
时间复杂度 \(\mathcal O(n^2)\)
Code
// Problem: C - Previous Permutation
// Contest: AtCoder - AtCoder Beginner Contest 276
// URL: https://atcoder.jp/contests/abc276/tasks/abc276_c
// Memory Limit: 1024 MB
// Time Limit: 2000 ms
//
// Powered by CP Editor (https://cpeditor.org)
#include <bits/stdc++.h>
using i64 = long long;
using pii = std::pair<int,int>;
#define fir first
#define sec second
#define Chtholly std::set<node>::iterator
const i64 mod = 1e9 + 7;
const int maxn = 5e5 + 5;
const int maxm = 3e6 + 5;
const int maxk = 30;
int n,m,k,a[maxn];
int main() {
scanf("%d",&n);
for(int i = 1;i <= n;++ i)
scanf("%d",&a[i]);
for(int i = 1;i <= n;++ i) {
bool flag = true;
for(int j = i + 2;j <= n;++ j) {
if(a[j] < a[j - 1]) {
flag = false;
break ;
}
}
if(!flag)continue ;
int pos = 0;
for(int j = i + 1;j <= n;++ j) {
if(a[j] < a[i]) {
if(a[j] > a[pos])pos = j;
}
}
std::swap(a[i] , a[pos]);
std::sort(a + i + 1 , a + n + 1 , std::greater<int>());
for(int j = 1;j <= n;++ j)printf("%d ",a[j]);
return 0;
}
return 0;
}
D - Divide by 2 or 3
给定 \(n\) 个数 \(a_1\sim a_n\),一次操作可以选择一个数除以 2 或 3(前提是这个数是 2 或 3 的倍数),问使得所有数都相等的最小操作次数。
显然,所有数相等的最优方案是它们的最大公约数。
把最大公约数求出来,剩下的部分拆分,如果能完全分解为 2,3 则可行,输出次数。
时间复杂度 \(\mathcal O(n\log a_i)\)
E - Round Trip
给定一张 \(n\times m\) 的网格图,其中有障碍和可行点,其中一个点是起点。问是否能从起点出发不经过重复的点再回到起点。
\(1\le n\times m\le 10^6\)
直接从四个方向 DFS 即可,用了 std::map
,故时间复杂度为 \(\mathcal O(nm\log nm)\)。
Code
// Problem: E - Round Trip
// Contest: AtCoder - AtCoder Beginner Contest 276
// URL: https://atcoder.jp/contests/abc276/tasks/abc276_e
// Memory Limit: 1024 MB
// Time Limit: 2000 ms
//
// Powered by CP Editor (https://cpeditor.org)
#include <bits/stdc++.h>
using i64 = long long;
using pii = std::pair<int,int>;
#define fir first
#define sec second
#define Chtholly std::set<node>::iterator
const i64 mod = 1e9 + 7;
const int maxn = 5e5 + 5;
const int maxm = 3e6 + 5;
const int maxk = 30;
int n,m,s,t,fx[] = {0 , -1 , 0 , 1},fy[] = {1 , 0 , -1 , 0};
std::map<int,bool> vis[maxn],inq[maxn];
bool dfs(int x,int y,int dep) {
if(inq[x][y]||vis[x][y])return false;
if(x == s&&y == t&&dep > 1)return true;
vis[x][y] = true;
for(int k = 0;k < 4;++ k) {
int nx = x + fx[k],ny = y + fy[k];
if(nx >= 1&&nx <= n&&ny >= 1&&ny <= m) {
if(nx == s&&ny == t&&!dep)continue ;
if(inq[nx][ny]||inq[x][y])continue ;
if(dfs(nx , ny , dep + 1))return true;
}
}
return false;
}
int main() {
scanf("%d %d",&n,&m);
for(int i = 1;i <= n;++ i) {
char c;
for(int j = 1;j <= m;++ j) {
scanf(" %c",&c);
if(c == 'S') {
s = i;
t = j;
}
else if(c == '#')inq[i][j] = true;
}
}
for(int k = 0;k < 4;++ k) {
for(int i = 1;i <= n;++ i)
vis[i].clear();
int x = s + fx[k],y = t + fy[k];
if(x >= 1&&x <= n&&y >= 1&&y <= m) {
if(dfs(x , y , 0)) {
puts("Yes");
return 0;
}
}
}
puts("No");
return 0;
}
F - Double Chance
给定 \(n\) 个正整数 \(a_1\sim a_n\),对于 \(k=1,2,\dots,n\),你需要:
两次从 \(a_1\sim a_k\) 中均等概率地选取一个取出,记录数值并放回。
你的得分为两次取出数值的较大值。
对于每个 \(k\),求出得分的期望值。
\(1\le n,a_i\le 2\times 10^5\)
之前在 Froggy 的 ARC 题解中看到过一道类似的题目,然后就照着那道题瞎搞,结果崩了 /kk,浪费了好多时间。
先将期望转化为总和除以总次数,总次数显然为 \(k^2\),只考虑计算总和。
要注意到 \(a_i\) 的小范围,正解显然跟这个有关。
令 \(ans=\sum\limits_{j=1}^i a_j\),考虑新加入一个 \(a_i\) 对答案的贡献(两次选的下标相同的情况由 \(ans\) 计算,此处仅考虑两次下标取的不同的情况):
记 \(x\) 为除 \(a_i\) 外另选的一个数。
-
当 \(x\le a_i\) 时,\(a_i\) 对答案产生贡献。
-
当 \(x\gt a_i\) 时,\(x\) 对答案产生贡献。
用值域树状数组统计出 \(\le a_i\) 的数的数量及 \(\gt a_i\) 的数的总和,分别记为 \(res,val\),则当前答案为:
\[\frac{ans+2\times(a_i\times res+val)}{i^2} \]此处 \(2\times\) 是因为操作的顺序也有影响。
时间复杂度 \(\mathcal O(n\log n)\)
Code
// Problem: F - Double Chance
// Contest: AtCoder - AtCoder Beginner Contest 276
// URL: https://atcoder.jp/contests/abc276/tasks/abc276_f
// Memory Limit: 1024 MB
// Time Limit: 2000 ms
//
// Powered by CP Editor (https://cpeditor.org)
#include <bits/stdc++.h>
using i64 = long long;
using pii = std::pair<long long,int>;
#define fir first
#define sec second
#define Chtholly std::set<node>::iterator
const i64 mod = 998244353;
const int maxn = 5e5 + 5;
const int maxm = 3e6 + 5;
const int maxk = 30;
int n,m,k,a[maxn];
i64 power(i64 x,i64 y) {
x %= mod;
i64 ans = 1;
for(;y;y >>= 1) {
if(y & 1)(ans *= x) %= mod;
(x *= x) %= mod;
}
return ans;
}
int cnt,c[maxn];
i64 sum[maxn];
int lowbit(int x) {
return x & -x;
}
void add(int x,int y) {
for(;x <= cnt;x += lowbit(x))
++ c[x],sum[x] += y;
return ;
}
int querynum(int x) {
int res = 0;
for(;x;x -= lowbit(x))
res += c[x];
return res;
}
i64 querysum(int x) {
i64 ans = 0;
for(;x;x -= lowbit(x))
ans += sum[x];
return ans;
}
int main() {
scanf("%d",&n);
cnt = 2e5;
i64 ans = 0,val = 0;
for(int i = 1;i <= n;++ i) {
scanf("%d",&a[i]);
(ans += a[i]) %= mod;
(val += 2ll * (1ll * a[i] * querynum(a[i]) % mod + 1ll * (querysum(cnt) - querysum(a[i]))) % mod) %= mod;
printf("%lld\n",(ans + val) % mod * power(1ll * i * i % mod , mod - 2) % mod);
add(a[i] , a[i]);
}
return 0;
}
G - Count Sequences
构造一个长度为 \(n\) 的序列 \(a\),满足:
\(0\le a_1\le a_2\le \dots \le a_n\le m\)
\(\forall i\in [1,n),a_i\not\equiv a_{i+1}\pmod{3}\)
求构造方案数。
\(1\le n,m\le 10^7\)
翻了翻 bot 和其他几位大佬的代码,好像没有我这么写的,讲一讲我的想法。
我们考虑构造 \(a\) 的差分数组 \(b\),这样只需要满足 \(\sum\limits_{i=1}^n b_i\le m\)。
只是这样的话也没什么意义,还有合法性的问题,考虑只用 1,2 填充数组,这样构造的序列一定是合法的,之后我们再把 3 用插板法加到数组里的某些位置即可。
然后我考场上跟个 zz 一样,一直以为不用插板法,快速幂也是可以的 qwq。
具体而言,枚举 2 的数量,记为 \(i\),且满足 \(i\times 2+(n-i)=i+n\le m\)。
此时的方案数为 \(\binom{n}{i}\times \sum\limits_{k=0}^{\lfloor\frac{m-n-i}{3} \rfloor} \binom{n+k-1}{n-1}\)。
后面那串式子和 \(i\) 没关系,直接预处理出来即可。
此时还要注意到,可能会有 \(b_1\equiv 0\pmod{3}\) 的情况,但我们强行用 1,2 填充了序列,这样会漏算。
这个问题也很简单,强令 \(b_1\) 初始为 0,然后解决 \(n-1\) 的子问题即可。
时间复杂度 \(\mathcal O(n+m)\)。
Code
// Problem: G - Count Sequences
// Contest: AtCoder - AtCoder Beginner Contest 276
// URL: https://atcoder.jp/contests/abc276/tasks/abc276_g
// Memory Limit: 1024 MB
// Time Limit: 4000 ms
//
// Powered by CP Editor (https://cpeditor.org)
#include <bits/stdc++.h>
using i64 = long long;
const i64 mod = 998244353;
const int maxn = 2e7 + 5;
i64 power(i64 x,i64 y) {
if(y < 0)return 0;
i64 ans = 1;
for(;y;y >>= 1) {
if(y & 1)(ans *= x) %= mod;
(x *= x) %= mod;
}
return ans;
}
i64 frac[maxn],inv[maxn],pw[4000000];
i64 C(int n,int m) {
if(n < 0||m < 0||n < m)return 0;
return frac[n] * inv[m] % mod * inv[n - m] % mod;
}
int m;
i64 solve(int n,int QAQ) {
i64 ans = 0;
pw[0] = 1;
for(int i = 1;i <= m / 3;++ i) {
pw[i] = (pw[i - 1] + C(QAQ + i - 1 , QAQ - 1)) % mod;
}
for(int i = 0;i <= n;++ i) {
if(i + n > m)break ;
(ans += C(n , i) * pw[(m - n - i) / 3] % mod) %= mod;
}
return ans;
}
int main() {
int n;
scanf("%d %d",&n,&m);
frac[0] = 1;
for(int i = 1;i <= n + m;++ i)
frac[i] = 1ll * (i64)i * frac[i - 1] % mod;
inv[n + m] = power(frac[n + m] , mod - 2);
for(int i = n + m - 1;~ i;-- i)
inv[i] = 1ll * (i64)(i + 1) * inv[i + 1] % mod;
printf("%lld\n",(solve(n , n) + solve(n - 1 , n)) % mod);
return 0;
}
题外话,还有两周左右 NOIp2022 就要开考了,但是 HA 这疫情形势真的还能考吗 QAQ。
标签:std,AtCoder,le,const,int,题解,i64,276,mod From: https://www.cnblogs.com/663B/p/16861788.html