「模拟赛」暑期集训CSP提高模拟6(7.23)

\(140 pts,Rank 23\)

题目列表

A.花间叔祖
B.合并 r
C.回收波特
D.斗篷


花间叔祖

\(98pts\)

题意:

给定一个数组,选择一个大于等于 2 的模数,然后把数组中的数变成 \(mod\) 该模数后的数。

只能操作一次,问操作后最少有几种不同的数。

赛时分析:

开始 5 分钟想到了算 \(a_i\) 中所有数的最小质因数,若不是 1,那么答案为 1,否则为 2,觉得很对,测大样例,WA,??自己觉得思路很对,由于大样例锅了很多次了,所以怀疑是不是大样例的问题,就先跳了,等着看改不改大样例。半个小时之后,回来,还没改,这应该不是大样例的问题了,于是拿大样例调试,果然发现了可以使答案为 1,想法假了!于是自己手模小样例发现问题所在,列出式子,直接出来了。结果少考虑一个点,导致没拿满.

正解:

容易得到答案不是 1 就是 2,若存在一个模数使得 \(a_i(i\in[1,n])\) mod 该数都为 \(d\),那么有 \(k_i*x+d=a_i\)\(x\) 为模数),那么 \(a_i-a_j \ (a_i>a_j)\) 化简可以得到 \(a_i-a_j=(k_i-k_j)*x\),那么我们只要拿 \(a\) 数组中的数两两作差得到 \(b\) 数组判有无公因数即可。

赛时我拿 \(a\) 数组排序后相邻的两个数相减没考虑可能为 0 的情况所以挂了 2pts。

code:

#include<bits/stdc++.h>
#define mp make_pair
#define ll long long
using namespace std;

const int N = 2e5 + 10;

int n, a[N], Ygcd, cnt, Xgcd;
int b[N];

signed main(){
	// freopen("in.in", "r", stdin); freopen("out.out", "w", stdout);

	scanf("%d%d%d", &n, &a[1], &a[2]);
	
	Ygcd = __gcd(a[1], a[2]);
	for(int i=3; i<=n; i++){
		scanf("%d", &a[i]);
		Ygcd = __gcd(Ygcd, a[i]);
	}

	if(Ygcd != 1){
		puts("1"); return 0;
	}

	sort(a+1, a+1+n);
	for(int i=2; i<=n; i++){
		b[++cnt] = a[i] - a[1];
	}

	Xgcd = __gcd(b[1], b[2]);
	for(int i=2; i<=cnt; i++){
		Xgcd = __gcd(Xgcd, b[i]);
	}

	if(Xgcd != 1) puts("1");
	else puts("2");

	return 0;
}

合并 r

题意:

\(n\) 个神奇的 \(r\),每个 \(r\) 有一个美味值,美味值都能表示为 \(\frac{1}{2^i}\),其中 \(i∈N\)(自然数集)。

Delov 一口吃掉了美味值之和为 \(k\)\(n\)\(r\), 现在他想知道每个 \(r\) 的美味值,但是可能的结果太多了,于是他只想知道有多少种可能性,你只需要告诉他方案数  \(mod 998244353\) 的结果。

两种方案不同,则存在 \(i\), 使得美味值为 \(\frac1{2^i}\)\(r\) 的个数在两种方案中不同。

正解:

原题 \(ARC107D\)

\(f_{i, j}\) 表示 \(i\) 个数和为 \(j\) 的方案数

我们这样考虑,\(\frac{1}{2^i} = 1 / 2 / 2 ....\)

就是说,当前如果凑成了 \(j\),那么所有数除以二,就能凑成 \(j / 2\)

那么我们只需两种操作,加一与除二,加一代表多选了一个数(初始为 \(1\) ),除二代表将原方案选的数中二的幂次都加一

\(f_{i, j} = f_{i - 1, j - 1} + f_{i, j * 2}\)

初始化 \(f_{0,0} = 0\)

code:

#include<bits/stdc++.h>
using namespace std;

const int N = 5e3 + 10;
const int p = 998244353;

int n, k;
int f[N][N];

int main(){
	// freopen("in.in", "r", stdin); freopen("out.out", "w", stdout);
	ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);

	cin>>n>>k;

	f[0][0] = 1;
	for(int i=1; i<=n; i++){
		for(int j=i; j>=1; j--){
			f[i][j] = f[i-1][j-1];
			if(j * 2 <= i) f[i][j] += f[i][j*2];
			f[i][j] %= p;
		}
	}
	cout<<f[n][k];

	return 0;
}

回收波特:

题意:

Delov 不和 npy、 们玩了,这回他和他的波特们一起玩。

他和他的 \(n\) 个波特们在一条道路上的不同地点,不妨认为这是一条数轴,而 Delov 位于原点。不幸的是波特们都快没电了,现在 Delov 要开始回收波特了。

Delov 只有一个控制所有波特的遥控器,所以每次他会给出一个参数 \(di\) ,波特就会向着目前 Delov 相对它所在的方向移动 \(di\) 的单位长度,恰好到达原点的波特会被回收不再移动。

在发送了 \(m\) 条指令后波特们没电了,现在 Delov 想知道每个波特是否被回收,如果被回收,他想知道该波特是在第几个指令后被回收,便于充电;如果没被回收,他想知道该波特最终处于的位置,便于手动回收。

你能帮帮他吗?

赛时分析:

一开始一眼就觉得会了,二分思路,时间复杂度正确,然后大喊一句“正解”开打,打了一半二分发现假了。。。于是就先按暴力 \(30pts\) 打,在打假的二分上套了一层二分,最优情况 \(O(m \log^2n)\),最坏 \(O(mn\log\ n)\),打完怕以为时间内复杂度不稳定连该拿的暴力分都拿不到,于是加了个 if(n*m<=200000000)然后跟暴力代码,否则跟玄学复杂度代码,最终 \(32 pts\),赛后交上只有玄学复杂度代码 \(42pts\),。。。

正解:

(复制的下发题解思路较简单)

官方题解写的很棒,但是是英语,英语好的/使用翻译的同学可以移步

粘个官方的图

image

注意到值域有限,考虑对值域内所有数处理出答案

观察一次操作过后,一部分超过原点,一部分到达原点,一部分没过原点,

到达的不用管而超过的部分,后续操作和关于原点对称的位置的操作一模一样,于是可以合并

每次将正半轴负半轴较短的一截与另一边合并起来

这样每个点只会操作 \(o(1)\) 次,复杂度就可以保证了。

code:

#include<bits/stdc++.h>
using namespace std;

const int N = 1e6 + 10;

int n, m, vis[N], ans[N];
int x[N], d[N];
std::vector<int>v[N], G;

void dfs(int x, int flag){
	for(auto y : v[x]){
		vis[y] = vis[x], ans[y] = -1.0 * flag * ans[x];
		dfs(y, flag);
	}
}

int main(){
	// freopen("in.in", "r", stdin); freopen("out.out", "w", stdout);
	ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);

	cin>>n>>m;
	for(int i=1; i<=n; i++) cin>>x[i];
	for(int i=1; i<=m; i++) cin>>d[i];


	int l = 1, r = N - 10, pos = 0, f = 1;//f=1:0 in the left
	for(int i=1; i<=m; i++){
		int D = d[i];
		pos += f * D; // now 0's position
		
		if(pos > r) f = -1;
		else if(pos < l) f = 1;
		else{
			vis[pos] = true, ans[pos] = i;
			G.push_back(pos);

			int lnum = pos - l, rnum = r - pos;
			if(lnum > rnum){
				for(int j=1; j<=r-pos; j++) //connect the side
					v[pos-j].push_back(pos+j);
				r = pos - 1, f = -1;
			}
			else{
				for(int j=1; j<=pos-l; j++)
					v[pos+j].push_back(pos-j);
				l = pos + 1, f = 1;
			}
		}
	}

	for(int i=l; i<=r; i++){
		ans[i] = i - pos;
		dfs(i, 1);
	}

	for(int i : G) dfs(i, -1);
	for(int i=1; i<=n; i++){
		if(vis[x[i]]) printf("Yes");
		else printf("No");
		printf(" %d\n", ans[x[i]]);
	}

	return 0;
}

斗篷

posted @ 2024-07-25 20:52  Aqr_Rn  阅读(106)  评论(7编辑  收藏  举报