首页 > 其他分享 >AtCoder Beginner Contest 276 A~G 题解

AtCoder Beginner Contest 276 A~G 题解

时间:2022-11-06 00:45:27浏览次数:106  
标签:std AtCoder le const int 题解 i64 276 mod

今天凌晨 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

相关文章

  • 个人比赛实况和题解
    CodeforcesCF1740:实况:https://pan.baidu.com/s/1BYjUp2Atvqkpqe3jVogPTQ(提取码:1104);题解:https://www.cnblogs.com/lucius7/p/16861747.html。AtCoderabc275:实况:https://p......
  • 洛谷P8757 [蓝桥杯 2021 省 A2] 完美序列 题解
    思路为使描述方便,先令题目描述中的“完美序列”反转(即序列单调递增且每一个数都是上一个数的倍数)。原“完美序列”与反转后的本质相同。先考虑最大长度。显然,当完美序列......
  • 洛谷P8775 [蓝桥杯 2022 省 A] 青蛙过河 题解 贪心+二分答案
    题目链接​​https://www.luogu.com.cn/problem/P8775​​题目大意小青蛙住在一条河边,它想到河对岸的学校去学习。小青蛙打算经过河里的石头跳到对岸。河里的石头排成了一条......
  • 【atcoder abc276 】(a* 搜索)
    importjava.io.BufferedReader;importjava.io.IOException;importjava.io.InputStreamReader;importjava.util.*;/****@autho......
  • 洛谷-P8814 题解
    前言蒟蒻感叹!许多大佬的思路真的很复杂,于是为了部分没学过的人写了这篇题解(其实就是为了咕值),值得一看!正文♦算法:式子推导从题中可得\(\begin{cases}n_i=p_i\times......
  • 「题解报告」[POI2008]PER-Permutation
    「题解报告」[POI2008]PER-Permutation点击查看目录目录「题解报告」[POI2008]PER-Permutation思路代码不理解哪里难了,学过扩卢并且推一下式子基本就是两眼切吧。......
  • Luogu P5816[CQOI2010]内部白点题解
    LinkLuoguP5816Description一个平面直角坐标系内有\(n\)个黑点,其余点为白点,将会进行若干次变换,每次变换会把上下左右方向都有黑点的白点变成黑点,直到找不到符合要求......
  • python print 打印延迟问题解决
    转载:https://wenku.baidu.com/view/ffc89347bb4ae45c3b3567ec102de2bd9705de56.html?wkts=1667639107060&bdQuery=python+print%E7%AB%8B%E5%8D%B3%E6%89%93%E5%8D%B0......
  • 【题解】洛谷P2946 [USACO09MAR]Cow Frisbee Team S
    这题乍一看是一个朴素的01背包问题,将所有方案的集合按照能力值的和来划分成1~m,然后把m当作体积,把n当作物品数,做一个01背包的代码。于是我兴冲冲地写了代码交上去,然后十组样......
  • accoders NOI #5047. 猜数游戏 题解
    题目描述Alice和Bob玩游戏。Alice有一个\(1~n\)中的正整数\(y\)。Bob不知道这个数。游戏中的每一轮,Bob选一个正整数\(x\),并提问Alice:\(y\)是否大于等于\(x......