CF刷题(01)——难度1600

从今天起,cf开刷,先从难度1600写起试试,每个难度的题目放在一个博客里面。看看最后可以写多少个博客。每一道AC的题目的代码都放在这里 😃
1600打算写25道题后晋级1700.

最近训练重心:数学

math、number theory、probabilities、geometry、combinatorics

每天至少3道题:见到这几个标签就去写!

1.C. k-Tree

  • 太菜了,不会写。官方的题解写的挺好的。
  • f ( i , j ) f(i, j) f(i,j):路径权重总和是 i,j = 0 表示路径中没有超过 d 的边,j = 1 表示路径中有超过 d 的边。
  • 我们这样思考,对于计算 f ( i , 0 ) f(i, 0) f(i,0), 我们让这个分为两步,先走 k,再一步走了 n - k,因此 f ( i , 0 ) = f ( i − 1 , 0 ) + f ( i − 2 , 0 ) + . . . + f ( i − m i n ( d − 1 , i ) , 0 ) f(i, 0) = f(i - 1, 0) + f(i - 2, 0) + ... + f(i - min(d - 1, i), 0) f(i,0)=f(i1,0)+f(i2,0)+...+f(imin(d1,i),0)。这样子首先是不会重复的,因为最后一步走的距离不一样,肯定不会一样。这样子应该也不会有遗漏的吧,因为…想不到会遗漏哪种情况,最后一步都枚举过了。
  • f ( i , 1 ) = f ( i − 1 , 1 ) + . . . + f ( i − m i n ( i , k ) , 1 ) + f ( i − d , 0 ) + f ( i − ( d + 1 ) , 0 ) + . . . + f ( i − m i n ( k , i ) , 0 ) f(i, 1) = f(i - 1, 1) + ... + f(i - min(i, k), 1) + f(i - d, 0) + f(i - (d + 1), 0) + ... + f(i - min(k, i), 0) f(i,1)=f(i1,1)+...+f(imin(i,k),1)+f(id,0)+f(i(d+1),0)+...+f(imin(k,i),0).
#include<cstdio>
#include<algorithm>
using namespace std;
const int maxn = 110;
int f[maxn][maxn], N, K, D;
const int mod = 1e9 + 7;
int main(){
	scanf("%d%d%d", &N, &K, &D);
	f[0][0] = 1;
	for(int i = 0; i <= N; i++){
		for(int j = 1; j <= min(D - 1, i); j++){
			f[i][0] = (f[i][0] + f[i - j][0]) % mod;
		}
	}
	for(int i = 0; i <= N; i++){
		for(int j = 1; j <= min(i, K); j++){
			f[i][1] = (f[i][1] + f[i - j][1]) % mod;
		}
		for(int j = D; j <= min(i, K); j++){
			f[i][1] = (f[i][1] + f[i - j][0]) % mod;
		}
	}
	printf("%d\n", f[N][1]);
	return 0;
}

2.A. Linova and Kingdom

  • 太菜了,不会写。但是官方的题解写的挺好懂的。
  • 答案思路没问题,但是公式写错了。我说怎么理解不了那个公式!
  • 应该这么说:the total happiness will be increased by s i z u − d e p u − 1 siz_u−dep_u - 1 sizudepu1: the envoy of u won’t be sent, we will lose d e p u dep_u depu happiness; a total of s i z u − 1 siz_u−1 sizu1 envoys of all nodes in the subtree rooted on u except u itself will get one more happiness.
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<functional>
using namespace std;
const int maxn = 200010, maxm = maxn * 2;
typedef long long ll;
int h[maxn], e[maxm], ne[maxm], idx;
int N, K;
int d[maxn], happiness[maxn];
void add(int a, int b){
	e[idx] = b, ne[idx] = h[a], h[a] = idx++;
}
int dfs(int u, int fa){
	int res = 1;
	for(int i = h[u]; i != -1; i = ne[i]){
		int v = e[i];
		if(v == fa) continue;
		d[v] = d[u] + 1;
		res += dfs(v, u);
	}
	happiness[u] = res - d[u] - 1;
	return res;

}
int main(){
	scanf("%d%d", &N, &K);
	memset(h, -1, sizeof h);
	for(int i = 1; i < N; i++){
		int a, b;
		scanf("%d%d", &a, &b);
		add(a, b), add(b, a);
	}
	dfs(1, -1);
	sort(happiness + 1, happiness + N + 1, greater<int>());
	ll ans = 0;
	for(int i = 1; i <= N - K; i++) ans += happiness[i];
	printf("%lld\n", ans);
	return 0;
}

3.A. Maze

这道题,我想这是什么玩意儿,想半天不知道怎么写。然后看一下题解…😂

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
int N, M, K;
const int maxn = 510;
char maze[maxn][maxn];
int dx[] = {1, -1, 0, 0}, dy[] = {0, 0, 1, -1};
int cnt, blank;
void dfs(int x, int y){
	maze[x][y] = '*';
	for(int i = 0; i < 4; i++){
		int nx = x + dx[i], ny = y + dy[i];
		if(nx < 0 || nx >= N || ny < 0 || ny >= M || maze[nx][ny] != '.') continue;
		if(++blank <= cnt - K) dfs(nx, ny);
	}
}
int main(){
	scanf("%d%d%d", &N, &M, &K);
	for(int i = 0; i < N; i++){
		scanf("%s", maze[i]);
	}

	for(int i = 0; i < N; i++){
		for(int j = 0; j < M; j++){
			if(maze[i][j] == '.') cnt++;
		}
	}

	bool flag = false;
	for(int i = 0; i < N; i++){
		for(int j = 0; j < M; j++){
			if(maze[i][j] == '.' && ++blank <= cnt - K){
				dfs(i, j);
				flag = true;
				break;
			}
		}
		if(flag) break;
	}


	for(int i = 0; i < N; i++){
		for(int j = 0; j < M; j++){
			if(maze[i][j] == '*') maze[i][j] = '.';
			else if(maze[i][j] == '.') maze[i][j] = 'X';
		}
	}
	for(int i = 0; i < N; i++) printf("%s\n", maze[i]);
	return 0;
}

4.奇怪的八进制

在这里插入图片描述
对于这样的n进制,其实可以这样。十进制数x:

  • stack.push((x - 1) % 8)
  • x = (x - 1) / 8.
dic = {0:'A', 1:'B', 2:'C', 3:'D', 4:'E', 5:'F', 6:'G', 7:'H'};
def get(x):
    if(x > 0):
        get((x - 1) // 8);
        print(dic[(x - 1) % 8], end = '');

x = int(input());
get(x);

5.D. Constructing the Array

  • 给定一个长度为n的全0数组a,每次进行以下操作直到所有元素均不为零:在第i次操作中,取最长的全为0的一段子序列(优先取最左边的),令 a [ l e n / 2 ] = i a[len/2]=i a[len/2]=i 。其中len为偶数,取l+r或l+r−1。
#include<cstdio>
#include<algorithm>
#include<queue>
using namespace std;
struct P {
	int l, r;
	P(int l_ = 0, int r_ = 0): l(l_), r(r_){}
	bool operator < (const P& rhp)const {
		return r - l < rhp.r - rhp.l ||  r - l == rhp.r - rhp.l && l > rhp.l;
	}
};
const int maxn = 200010;
int a[maxn], N;
priority_queue<P> que;
int main(){
	int T;
	scanf("%d", &T);
	while(T--){
		scanf("%d", &N);
		
		que.push(P(0, N - 1));
		int cnt = 0;
		while(que.size()){
			auto p = que.top(); que.pop();
			int mid = (p.r + p.l) / 2;
			a[mid] = ++cnt;
			
			if (mid - 1 >= p.l) {
				que.push(P(p.l, mid - 1));
			}
			if (p.r >= mid + 1) {
				que.push(P(mid + 1, p.r));
			}
		}
		for(int i = 0; i < N; i++){
			printf("%d%c", a[i], i + 1 == N ? '\n' : ' ');
		}
	}
	return 0;
}

6.C. Game On Leaves

  • 我想,如果你想等到X变成叶子节点的时候再去取,那么是两个情况:它本身就是叶子节点;原图只剩下两个节点了。因为那两个人会想方设法不让它成为叶子节点。
  • 小心变量别整错。
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
const int maxn = 1010, maxm = 2010;
int N, X, deg[maxn];

int main(){
	int T;
	scanf("%d", &T);
	while(T--){
		scanf("%d%d", &N, &X);
		memset(deg, 0, sizeof deg);
		for(int i = 1; i < N; i++){
			int a, b;
			scanf("%d%d", &a, &b);
			deg[a]++, deg[b]++;
		}
		if(deg[X] <= 1 || (N - 1) & 1) printf("Ayush\n");
		else printf("Ashish\n");
	}
	return 0;
}

7.C. Circle of Monsters

  • 从大角度分析。首先,怪物如果死后不爆炸,那么需要 A = ∑ i = 0 N − 1 a i A = \sum\limits_{i=0}^{N - 1}a_i A=i=0N1ai 个子弹。但是,由于爆炸,可以节省 B = ∑ i = 0 N − 1 m i n ( G [ i ] . b , G [ ( i + 1 ) % N ] . a ) − x B = \sum\limits_{i=0}^{N-1}min(G[i].b, G[(i+1)\%N].a) - x B=i=0N1min(G[i].b,G[(i+1)%N].a)x 的子弹。A 和 B 都是定值,因此x越小越好。于是程序如下:
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
const int maxn = 300010;
typedef long long ll;
struct P{
	ll a, b;
}G[maxn];
int N;
int main(){
	int T;
	scanf("%d", &T);
	while(T--){
		scanf("%d", &N);
		
		for(int i = 0; i < N; i++){
			scanf("%lld%lld", &G[i].a, &G[i].b);
		}

		ll A = 0, B = 0, max_B = 1e18;
		for(int i = 0; i < N; i++){
			A += G[i].a, B += min(G[i].b, G[(i + 1) % N].a);
			max_B = min(min(G[i].b, G[(i + 1) % N].a), max_B);
		}
		printf("%lld\n", A - B + max_B);
	}
	return 0;
}

8.G. Special Permutation

  • 无脑构造题,挺好写的。方法略微有点麻烦,但是解决问题第一步嘛。
#include<cstdio>
#include<algorithm>
using namespace std;
const int maxn = 1010;
int a[maxn], N;

int dx[5][10] = { {1, 3, 5, 2, 4}, {1, 3, 6, 4, 2, 5}, {1, 3, 7, 4, 6, 2, 5},
	{1, 3, 5, 7, 4, 8, 6, 2}, { 1, 3, 5, 7, 9, 6, 8, 4, 2 } };
int main(){
	int T;
	scanf("%d", &T);
	while(T--){
		scanf("%d", &N);
		int m1 = N / 5, m2 = N % 5;
		bool flag = true;
		if(N == 2 || N == 3){
			flag = false;
		}
		else if(N == 4){
			a[0] = 2, a[1] = 4, a[2] = 1, a[3] = 3;
		}
		else{
			for (int i = 0; i < (m1 - 1) * 5; i++) {
				int k1 = i / 5, k2 = i % 5;
				a[i] = k1 * 5 + dx[0][k2];
			}
			for(int i = (m1 - 1) * 5; i < N; i++){
				a[i] = dx[m2][i - (m1 - 1) * 5] + (m1 - 1) * 5;
			}
		}
		if(!flag){
			printf("-1\n");
			continue;
		}
		for(int i = 0; i < N; i++){
			printf("%d%c", a[i], i + 1 == N ? '\n' : ' ');
		}
	}
	return 0;
}

9.C. Good Subarrays

你需要找到有多少个区间使得其区间内每个元素的和为区间长度。

  • Let’s precalculate the array p, p r − p l − 1 = r − l + 1 ↔ p r − r = p l − 1 − ( l − 1 ) p_r - p _{l-1} = r - l + 1 \leftrightarrow p_r - r = p_{l - 1} - (l - 1) prpl1=rl+1prr=pl1(l1).
  • 别忘记 p 0 p_0 p0 也要算上。
#include<cstdio>
#include<iostream>
#include<unordered_map>
#include<cstring>
using namespace std;
const int maxn = 100010;
int N, a[maxn];
unordered_map<int, int> cnt;
typedef long long ll;
int main() {
	int T;
	cin >> T;
	while (T--) {
		cnt.clear();
		cnt[0] = 1;
		char c;
		cin >> N;
		for (int i = 1; i <= N; i++) {
			cin >> c;
			a[i] = c - '0';
			a[i] += a[i - 1];
			cnt[a[i] - i]++;
		}
		ll ans = 0;
		for (auto p : cnt) {
			ll x = p.second;
			ans += (ll)x * (x - 1) / 2;
		}
		cout << ans << endl;
	}
	return 0;
}

10.C. Phoenix and Distribution

  • 给了一个长度为n的字符串,让你将字符串分成k个字符串,这k个字符串不能存在空串,现在想要知道,这k个字符串中 字典序最大的最小的字符串是什么。
  • 设最小的字符是 x,x 出现的次数是 cnt. 如果 cnt < K,那么就输出字典序第 K 小的字符;cnt >= K时,看看剩下的 N - K 个字符是否相等,如果相等,则均匀的分在每个字串后面,如果不相等,就全部接在 x 的后面。
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<unordered_map>
using namespace std;
const int maxn = 100010;
char s[maxn];
int N, K;
bool all_the_same(const char* s) {
	for (int i = 0; s[i]; i++) {
		if (s[i] != s[0]) return false;
	}
	return true;
}
int main() {
	int T;
	scanf("%d", &T);
	while (T--) {
		unordered_map<char, int> cnt;
		char minc = 'z';
		scanf("%d%d%s", &N, &K, &s);
		s[N] = 0;
		for (int i = 0; i < N; i++) {
			cnt[s[i]]++;
			if (minc > s[i]) minc = s[i];
		}
		sort(s, s + N);
		if (cnt[minc] < K) {
			printf("%c\n", s[K - 1]);
			continue;
		}
		else {
			printf("%c", minc);
			if (!all_the_same(s + K)) printf("%s\n", s + K);
			else {
				int left = strlen(s + K);
				for (int i = 0; i < (left + K - 1) / K; i++) {
					printf("%c", s[K]);
				}
				printf("\n");
			}
		}
	}
	return 0;
}

11.C. Yet Another Counting Problem

  • 题意:Calculate the number of integers x x x such that l i ≤ x ≤ r i l_i≤x≤r_i lixri, and ( ( x   m o d   a )   m o d   b ) ≠ ( ( x   m o d   b )   m o d   a ) ((x\ mod\ a)\ mod\ b)≠((x\ mod\ b)\ mod\ a) ((x mod a) mod b)=((x mod b) mod a).
  • 本来想推到数学公式,然后发现不会推。但是 a 和 b 的范围都挺小的。打个表很快发现规律,以 L C M ( a , b ) LCM(a, b) LCM(a,b) 为循环节,每个循环节内满足条件的数都相同,算出[1,r]满足条件的数,算出[1,l−1]满足条件的数,相减即可。又是这个方法!因为查询数很多,所以可以算出前缀和数组。
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
typedef long long ll;
const int maxn = 400010;
int res[maxn];
ll gcd(ll a, ll b) {
	if (b == 0) return a;
	return gcd(b, a % b);
}
ll A, B, lcm;
ll cal(ll x) {
	return x / lcm * res[lcm] + res[x % lcm];
}
int main() {
	int T;
	scanf("%d", &T);
	while (T--) {
		memset(res, 0, sizeof res);
		scanf("%lld%lld", &A, &B);
		lcm = A * B / gcd(A, B);

		for (int i = 1; i <= lcm; i++) {
			if (i % A % B != i % B % A) res[i] = 1;
			res[i] += res[i - 1];
		}
		int Q;
		scanf("%d", &Q);
		for(int i = 0; i < Q; i++){
			ll l, r;
			scanf("%lld%lld", &l, &r);
			printf("%lld%c", cal(r) - cal(l - 1), i + 1 == Q ? '\n' : ' ');
		}
	}
	return 0;
}

12.C. Hard problem

  • 要使n个字符串变为字典序。只能进行一种操作:将第i个字符串reverse,并消耗c[i]的能量。求消耗的最小的能量
  • f ( i , j ) f(i, j) f(i,j): 前 i i i 个字符串, j = 0 j = 0 j=0 表示第 i i i 个字符串没有反转, j = 1 j = 1 j=1表示字符串反转。其实可以当做一个状态机去写。
  • 然后就很好写了呀。不过有一个需要说清楚,按照字典序排序,可以出现小于等于的情况,不需要严格递增。
#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxn = 100010;
string nor[maxn], rev[maxn];
typedef long long ll;
ll c[maxn], f[maxn][2];
int N;
const ll INF = 0x3f3f3f3f3f3f3f3f;
int main() {
	cin >> N;
	for (int i = 1; i <= N; i++) {
		cin >> c[i];
	}
	for (int i = 1; i <= N; i++) {
		cin >> nor[i];
		rev[i] = nor[i];
		reverse(rev[i].begin(), rev[i].end());
	}
	memset(f, 0x3f, sizeof f);
	f[0][0] = f[0][1] = 0;
	for (int i = 1; i <= N; i++) {
		if (nor[i] >= nor[i - 1]) f[i][0] = min(f[i][0], f[i - 1][0]);
		if (nor[i] >= rev[i - 1]) f[i][0] = min(f[i][0], f[i - 1][1]);
		if (rev[i] >= nor[i - 1]) f[i][1] = min(f[i][1], f[i - 1][0] + c[i]);
		if (rev[i] >= rev[i - 1]) f[i][1] = min(f[i][1], f[i - 1][1] + c[i]);
	}
	ll ans = min(f[N][0], f[N][1]);
	if (ans == INF) cout << -1 << endl;
	else cout << ans << endl;
	return 0;
}

13.A. Orac and LCM

  • 一个长度为 N 的数组,求 g c d { l c m ( { a i , a j } ) ∣ i < j } gcd\{lcm(\{a_i , a_j\}) | i < j\} gcd{lcm({ai,aj})i<j}
  • Observation. p k ∣ a n s p^k ∣ ans pkans if and only if there are at least n − 1 n−1 n1 integers in a that s.t. p k ∣ a i p^k∣ a_i pkai.
  • g c d ( l c m ( a 1 , a 2 ) , l c m ( a 1 , a 3 ) , l c m ( a 1 , a 4 ) , … , l c m ( a 1 , a n ) ) gcd(lcm(a_1,a_2),lcm(a_1,a_3),lcm(a_1,a_4),…,lcm(a_1,a_n)) gcd(lcm(a1,a2),lcm(a1,a3),lcm(a1,a4),,lcm(a1,an))。因为都有一个共同因子 a 1 a_1 a1。所以 r e s 1 = l c m ( a 1 , g c d ( a 2 , a 3 , a 4 , … , a n ) res_1=lcm(a_1,gcd(a_2,a_3,a_4,…,a_n) res1=lcm(a1,gcd(a2,a3,a4,,an)。同理 g c d 2 gcd_2 gcd2 也是这样的。最终答案是 g c d ( r e s 1 , r e s 2 , r e s 3 , … , r e s n ) gcd(res_1,res_2,res_3,…,res_n) gcd(res1,res2,res3,,resn)。所以我们只要求出一个后缀gcd即可.
  • 其实还有一种方法,对每个数进行质因数分解,因为 a i a_i ai 的范围比较小。然后选出次数第二大的k作为 p k ∣ a n s p^k | ans pkans. 不过要特判一种情况。当有两个 a i a_i ai 都不能被 p 整除的时候,就把这个p舍去。
#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
typedef long long ll;
const int maxn = 100010;
int N;
ll a[maxn], GCD[maxn], res[maxn];

ll gcd(ll a, ll b) {
	if (b == 0) return a;
	return gcd(b, a % b);
}

ll lcm(ll a, ll b, ll g) {
	return a * b / g;
}

int main() {
	cin >> N;
	for (int i = 1; i <= N; i++) {
		cin >> a[i];
	}
	for (int i = N; i >= 1; i--) {
		GCD[i] = gcd(a[i], GCD[i + 1]);
	}
	for (int i = 1; i < N; i++) {
		res[i] = lcm(a[i], GCD[i + 1], gcd(a[i], GCD[i + 1]));
	}
	ll ans = res[1];
	for (int i = 2; i < N; i++) {
		ans = gcd(ans, res[i]);
	}
	cout << ans << endl;
}

14.C. Game with Chips

  • 2 ∗ N ∗ M 2*N*M 2NM 是一个非常大的数,因此可以先把所有chips移动到同一个格子2333…
  • 第一步,先把所有chips移动到 ( 1 , 1 ) (1,1) (1,1),然后把格子全部访问一遍就行。不过这个是不可以把目标点排序然后依次移动过去的。
#include<iostream>
#include<cstring>
#include<algorithm>
#include<vector>
using namespace std;
const int maxk = 210;
vector<char> ans;
int N, M, K;
int main() {
	scanf("%d%d%d", &N, &M, &K);
	for (int i = 0; i < 2 * K; i++) {
		int x, y;
		scanf("%d%d", &x, &y);
	}
	
	for (int i = 1; i < N; i++) ans.push_back('U');
	for (int i = 1; i < M; i++) ans.push_back('L');
	
	for (int i = 1; i <= N; i++) {
		if (i & 1) {
			for (int j = 1; j < M; j++) {
				ans.push_back('R');
			}
		}
		else {
			for (int j = 1; j < M; j++) {
				ans.push_back('L');
			}
		}
		ans.push_back('D');
	}
	ans.pop_back();
	cout << ans.size() << endl;
	for (auto u : ans) cout << u;
	cout << endl;
	return 0;
}

15.B. Array Walk

题意:有1-n个点,每个点赋值为a[i],现在可以移动k次,不过只能向左移动z次,并且不能连续向左移动,问你最后走过所有点的和最大是多少。

  • 最终的位置由向左走的步数决定。假设向左走了 t t t 步,则向右走了 k − t k - t kt 步。最后一定呆在 k − 2 ∗ t + 1 k - 2 * t + 1 k2t+1。就是把 1 1 1 k − 2 ∗ t + 1 k - 2 * t + 1 k2t+1 加起来,然后在 1 1 1 k − 2 ∗ t + 2 k - 2 * t + 2 k2t+2 中找到最大的 ( a i − 1 , a i ) (a_{i-1},a_{i}) (ai1,ai)。为什么是 k − 2 ∗ t + 2 k - 2 * t + 2 k2t+2 呢?因为可能最后最大的 pair 是 ( a k − 2 ∗ t + 1 , a k − 2 ∗ t + 2 ) (a_{k - 2 * t + 1}, a_{k - 2 * t + 2}) (ak2t+1,ak2t+2).
  • 其实可以先扫描一遍,维护一个前缀数组, r e s i res_i resi a k − 1 + a k a_{k - 1} + a_{k} ak1+ak 最大值,其中 k ≤ i k\le i ki. 当然还有一个前缀和数组。
#include<cstdio>
#include<algorithm>
#include<iostream>
#include<cstring>
using namespace std;
const int maxn = 100010;
int a[maxn], res[maxn];
int N, K, Z;
int main() {
	int T;
	scanf("%d", &T);
	while (T--) {
		scanf("%d%d%d", &N, &K, &Z);
		for (int i = 1; i <= N; i++) scanf("%d", &a[i]);
		int max_now = 0;
		for (int i = 1; i <= N; i++) {
			if (a[i] + a[i - 1] > max_now) max_now = a[i] + a[i - 1];
			res[i] = max_now;
		}
		for (int i = 1; i <= N; i++) {
			a[i] += a[i - 1];
		}
		int ans = 0;
		for (int t = 0; t <= Z; t++) {
			if (K - 2 * t + 1 < 1) break;
			int tmp = a[K - 2 * t + 1] + t * res[K - 2 * t + 2];
			ans = max(ans, tmp);
		}
		cout << ans << endl;
	}
	return 0;
}

16.A. LCM Challenge

  • 题意:找到3个不超过n的正整数(可以相同),使得它们的lcm(最小公倍数)最大。输出最大的lcm。
  • 找规律我们发现,对于任意一个正整数 a:a 和 a + 1 最大公因数都是1;当 a 是奇数的时候,a 和 a + 2 最大公因数是1;当 a 是偶数的时候,a 和 a + 2 最大公因数是2,a 和 a + 3 的最大公约数是 1 或者是 3,因此,当 N > 6 时, 则 ( N − 1 ) ∗ ( N − 2 ) ∗ ( N − 3 ) (N - 1) * (N - 2) * (N - 3) (N1)(N2)(N3) N ∗ ( N − 1 ) ∗ ( N − 2 ) / 2 N * (N - 1) * (N - 2) / 2 N(N1)(N2)/2 应该更大一些。
  • 题解的方法还是有点😂,只看 [max(1, n - 50), n]中间连续的三个数最大公因数是多少,持续更新答案。
#include<iostream>
#include<algorithm>
using namespace std;
typedef long long ll;
ll N;
ll gcd(ll a, ll b) {
	if (b == 0) return a;
	return gcd(b, a % b);
}
int main() {
	cin >> N;
	
	if (N == 1) cout << 1 << endl;
	else if (N == 2) cout << 2 << endl;
	else if (N & 1) cout << N * (N - 1) * (N - 2) << endl;
	else {
		ll ans = max(N * (N - 1) * (N - 2) / 2, N * (N - 1) * (N - 3) / gcd(N, N - 3));
		ans = max((N - 1) * (N - 2) * (N - 3), ans);
		cout << ans << endl;
	}
	return 0;
}
//int main() {
//	for (ll i = 6; i <= 1000; i+=2) {
//		printf("%lld %lld %lld\n", i, i - 3, gcd(i, i - 3));
//	}
//}

17. A. Multiples of Length

  • 题意:给定一个数列{a},每次你需要选中一个区间[L,R],然后对区间内每个数字进行一次操作,让ai变成ai+k,并且满足k mod (R−L+1)=0.你只能严格做三遍操作,使得最终所有数字变成0. 注意,不是说加上同一个数,区间内每个数字加上的数不需要相等。
  • 我就知道是这个味,但还是没有想到。其实,因为数不相同,所以加上的数字,集合区间长度联系上,有和 a i a_i ai 联系上,那就是这个方法了。
  • 把每一个数加上 − N ∗ a i -N* a_i Nai 就是 − ( N − 1 ) ∗ a i -(N - 1) * a_i (N1)ai,然后每个数再加上 ( N − 1 ) ∗ a i (N - 1)*a_i (N1)ai 就好了。
#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
typedef long long ll;
const int maxn = 100010;
int a[maxn], N;
int main() {
	cin >> N;
	for (int i = 1; i <= N; i++) cin >> a[i];
	if (N == 1) {
		printf("1 1\n0\n1 1\n0\n1 1\n%d\n", -a[1]);
	}
	else {
		printf("1 1\n%d\n", -a[1]);
		printf("1 %d\n", N);
		a[1] = 0;
		for (int i = 1; i <= N; i++) {
			printf("%lld%c", -(ll)N * (ll)a[i], i == N ? '\n' : ' ');
		}
		printf("2 %d\n", N);
		for (int i = 2; i <= N; i++) {
			printf("%lld%c", (ll)(N - 1) * (ll)a[i], i == N ? '\n' : ' ');
		}
	}
	return 0;
}

18. C. Kuroni and Impossible Calculation

  • 题意:给定n个整数和m,求 ∏ 1 ≤ i < j ≤ n ∣ a i − a j ∣ m o d ( m ) ∏_{1≤i<j≤n}|a_i−a_j|mod(m) 1i<jnaiajmod(m) 的值范围。性质: 2 ≤ n ≤ 2 × 1 0 5 , 1 ≤ m ≤ 1 0 3 , 0 ≤ a i ≤ 1 0 9 2≤n≤2×10^5,1≤m≤10^3,0≤a_i≤10^9 2n2×105,1m103,0ai109
  • 你发现 M 很小,最后结果要取 M,所以当 N > M 时,根据鸽巢原理,答案一定是0呀。
  • N <= M 时,直接暴力求解就行。
#include<cstdlib>
#include<iostream>
#include<algorithm>
using namespace std;
const int maxn = 200010;
typedef long long ll;
int N;
ll a[maxn], M;
int main() {
	cin >> N >> M;
	for (int i = 1; i <= N; i++) {
		cin >> a[i];
 	}
	if (N > M) {
		cout << 0 << endl;
	}
	else {
		ll ans = 1;
		for (int i = 1; i <= N; i++) {
			for (int j = i + 1; j <= N; j++) {
				ans = ans * abs(a[i] - a[j]) % M;
			}
		}
		cout << ans << endl;
	}
	return 0;
}

19. B. Ciel and Flowers

  • 这道题从剩下几朵花讨论会比较方便
  • 其实只会出现六种情况:000,001,002,011,012,022;最后一种需要注意是否需要把一束纯色花拆开,其他都不用管。
#include<iostream>
#include<vector>
#include<algorithm>
using namespace std;
typedef long long ll;
typedef pair<ll, ll> P;
P R, G, B;
vector<P> v;
int main() {
	cin >> R.second >> G.second >> B.second;
	R.first = R.second % 3, G.first = G.second % 3, B.first = B.second % 3;
	v.push_back(R), v.push_back(G), v.push_back(B);
	
	sort(v.begin(), v.end());
	ll res = R.second + G.second + B.second;
	while (v[0].first >= 1 && v[1].first >= 1 && v[2].first >= 1) {
		v[0].first--;
		v[1].first--;
		v[2].first--;
	}
	if (v[0].first == 0 && v[1].first == 2 && v[2].first == 2 && v[0].second >= 3) {
		cout << ((res - 1) / 3) << endl;
	}
	else {
		cout << ((res - v[0].first - v[1].first - v[2].first) / 3) << endl;
	}

	return 0;
}

20.C. Two Arrays

  • 题意:你被给予了两个整数n和m,计算两个数组对a,b的个数。数组需满足:
  1. 两个数组的长度都是m
  2. 数组中每一个整数都是1到n
  3. ai≤bi
  4. 数组a是一个非降的序列,数组b是一个非升的序列
  • 可以考虑将a,b数组连起来形成一个b[1],b[2],…,b[m],a[m],a[m-1],…,a[1]的数组,长度为2m且非严格递减,取值都在[1,n]之间(与原问题等价)。
  • 转化为 1 ≤ a 1 ≤ . . . ≤ a 2 m ≤ N 1 \le a_1 \le ... \le a_{2m} \le N 1a1...a2mN.
  • x 1 = a 1 , x 2 = a 2 − a 1 . . . , x 2 m = a 2 m − a 2 m − 1 x_1 = a_1, x_ 2 = a_2 - a_1..., x_{2m} = a_{2m} - a_{2m - 1} x1=a1,x2=a2a1...,x2m=a2ma2m1,则 x 1 + x 2 + . . . + x 2 m ≤ N x_1 + x_2 + ... + x_{2m} \le N x1+x2+...+x2mN
  • 令 z 1 = x 1 , z 2 = x 2 + 1 , . . . , z 2 m = x 2 m + 1 令 z_1 = x_1, z_2 = x_2 + 1, ..., z_{2m} = x_{2m} + 1 z1=x1,z2=x2+1,...,z2m=x2m+1,则 0 < z 1 + z 2 + . . . + z 2 m ≤ N 0 < z_1 + z_2 + ... + z_{2m} \le N 0<z1+z2+...+z2mN
  • 这样就转化为 2m 个隔板,N + 2m - 1 个空隙,答案就是 C N + 2 m − 1 2 m C_{N + 2m - 1}^{2m} CN+2m12m
#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
typedef long long ll;
const ll mod = 1e9 + 7;
ll N, M;
ll mod_pow(ll x, ll n) {
	ll res = 1;
	while (n) {
		if(n & 1) res = res * x % mod;
		x = x * x % mod;
		n >>= 1;
	}
	return res;
}

int main() {
	cin >> N >> M;
	ll ans = 1;
	for (ll i = N + 2 * M - 1; i >= N; i--) {
		ans = ans * i % mod;
	}
	for (ll i = 1; i <= 2 * M; i++) {
		ans = ans * mod_pow(i, mod - 2) % mod;
	}
	cout << ans << endl;
}

21.C. Chocolate Bunny

  • 交互题,询问 i , j i,j i,j p i   m o d   p j p_i \ mod\ p_j pi mod pj 的值,问最后这个长度为n的排列是什么。最多问 2*n 次。
  • 不要看漏是一个1~N的排列,不然就亏大了。
  • a mod b < b mod a,说明 a > b;因为,当 a > b 时,a mod b <= b - 1,而 b % a = b.
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxn = 10010;
int a[maxn], N;
int main() {
	int max_id = 1;
	scanf("%d", &N);
	for (int i = 2; i <= N; i++) {
		printf("? %d %d\n", i, max_id);
		fflush(stdout);
		int x1, x2;
		scanf("%d", &x1);
		printf("? %d %d\n", max_id, i);
		fflush(stdout);
		scanf("%d", &x2);
		if (x1 > x2) {
			a[i] = x1;
		}
		else if(x1 < x2){
			a[max_id] = x2;
			max_id = i;
		}
	}
	a[max_id] = N;

	printf("! ");
	fflush(stdout);
	for (int i = 1; i <= N; i++) {
		printf("%d%c", a[i], i == N ? '\n' : ' ');
		fflush(stdout);
	}
	return 0;
}

22.D2. Equalizing by Division (hard version)

  • 给了n个数 每次操作可以任意选一个数对他除以2向下取整代替这个数,问让这n个数中,至少有k个数一样。至少要多少次操作. 1 ≤ n , k ≤ 2 ∗ 1 0 5 . 1 \le n,k \le 2*10^5. 1n,k2105.
  • vector v[maxn], 记录把每一个元素变为 i 时的花费,push 进 v[i] 里面,然后找到满足 v[i].size() >= k 且总花费最小的。
#include<cstdio>
#include<vector>
#include<algorithm>
using namespace std;
const int maxn = 200010;
vector<int> v[maxn];
int N, K;

int main() {
	scanf("%d%d", &N, &K);
	for (int i = 0; i < N; i++) {
		int x;
		scanf("%d", &x);
		int cnt = 0;
		while (x > 0) {
			v[x].push_back(cnt);
			x >>= 1;
			cnt++;
		}
		v[0].push_back(cnt);
	}
	int ans = 1e9;
	for (int i = 0; i <= 200000; i++) {
		int res = 0;
		if (v[i].size() >= K) {
			sort(v[i].begin(), v[i].end());
			for (int j = 0; j < K; j++) res += v[i][j];
			ans = min(ans, res);
		}
	}
	printf("%d\n", ans);
}

23.B. New Year Permutation

  • 题目的意思是给n个数 a [ ] a[] a[],然后给 n ∗ n n*n nn 的关系矩阵 g [ ] [ ] g[][] g[][],如果关系矩阵中 a [ i ] [ j ] a[i][j] a[i][j] ==1,则代表a[i]和a[j]等价,可以交换。问经过交换能得到的字典序最小的序列为多少。
  • 这道题其实不难,模拟的感觉强了一点,很顺利。
#include<iostream>
#include<algorithm>
#include<cstring>
#include<cstdio>
using namespace std;
const int maxn = 310;
int g[maxn][maxn], N, a[maxn], ans[maxn];

void floyd() {
	for (int k = 1; k <= N; k++) {
		for (int i = 1; i <= N; i++) {
			for (int j = 1; j <= N; j++) {
				g[i][j] |= (g[i][k] & g[k][j]);
			}
		}
	}
}
int main() {
	cin >> N;
	for (int i = 1; i <= N; i++) {
		cin >> a[i];
	}
	for (int i = 1; i <= N; i++) {
		for (int j = 1; j <= N; j++) {
			char c;
			cin >> c;
			g[i][j] = c - '0';
		}
	}
	floyd();
	for (int u = 1; u <= N; u++) {
		int id;
		for (id = 1; id <= N; id++) {
			if (a[id] == u) break;
		}
		for (int i = 1; i <= N && i <= id; i++) {
			if (g[i][id] == 1 && !ans[i]) {
				ans[i] = u;
				swap(a[i], a[id]);
				break;
			}
			if (i == id) {
				ans[i] = u;
			}
		}
	}
	for (int i = 1; i <= N; i++) {
		printf("%d%c", ans[i], i == N ? '\n' : ' ');
	}
	return 0;
}

24.A. Little Pony and Expected Maximum

  • 题意:给定m,表示有一个m面的色子,每个面的值为1~m,概率相同,丢n次,问说最大值的期望。
  • 这道题蛮简单的。其实这种概率题都是可以转化为古典概型的啦。难点就在我们计算一个序列的个数(当序列中最大的元素是 i 时。
  • 答案就是: ∑ i = 1 m i ∗ i n − ( i − 1 ) n m n = ∑ i = 1 m i ∗ [ ( i m ) n − ( i − 1 m ) n ] . \sum\limits_{i=1}^{m}i * \frac{i^n-(i-1)^n}{m^n}=\sum\limits_{i=1}^{m}i*[(\frac{i}{m})^n-(\frac{i-1}{m})^n]. i=1mimnin(i1)n=i=1mi[(mi)n(mi1)n]. 等式的右边为了防止浮点数溢出。
#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
double qpow(double x, int n) {
	double res = 1;
	while (n) {
		if(n & 1) res *= x;
		x *= x;
		n >>= 1;
	}
	return res;
}
int N, M;
int main() {
	cin >> M >> N;
	double ans = 0;
	for (int i = 1; i <= M; i++) {
		ans += i * (qpow((double)i / (double)M, N) - qpow(((double)(i - 1) / (double)M), N));
	}
	printf("%.15f\n", ans);
	return 0;
}

25.A. Rational Resistance

  • 这个题有个规律,就是a/b,b/a所需要的电阻数量一样,只是串并联关系不一样而已,因此该题可以这样考虑:那么把每一个电阻都拆成 a b \frac{a}{b} ba a % b b \frac{a \% b}{b} ba%b.
  • 为什么 a b \frac{a}{b} ba b a \frac{b}{a} ab 所需电阻数量是一样的呢?If a fraction a b \frac{a}{b} ba can be obtained with k k k resistors, then it is simple to calculate that we can obtain a + b b \frac{a + b}{b} ba+b fractions and a a + b \frac{a}{a+b} a+ba with k   +   1 k + 1 k+1 resistors.
#include<iostream>
#include<algorithm>
using namespace std;
typedef long long ll;
ll a, b;
int main() {
	cin >> a >> b;
	ll ans = 0;
	while (b) {
		ans += a / b;
		a %= b;
		swap(a, b);
	}
	cout << ans << endl;
}
  • 4
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值