首页 > 其他分享 >【题解】Educational Codeforces Round 141(CF1783)

【题解】Educational Codeforces Round 141(CF1783)

时间:2023-09-13 09:23:04浏览次数:43  
标签:Educational le 题目 CF1783 int 题解 scanf 操作 now

评价:educational

A.Make it Beautiful

题目描述:

如果一个数组中存在一个数恰好等于该数前面所有数之和,那么这个数组就是丑的。如果一个数组不是丑的,就是美的。

比如说:

  • 数组 $ [6, 3, 9, 6] $ 是丑的,因为 \(9 = 6 + 3\) ;
  • 数组 $ [5, 5, 7] $ 是丑的,因为第二个 \(5 = 5\) 。
  • 数组 $ [8, 4, 10, 14] $ 是美的,因为 $ 8 \ne 0 $ , $ 4 \ne 8 $ , $ 10 \ne 8 + 4 $ , $ 14 \ne 8 + 4 + 10 $ ,没有任何一个数等于它前面的数之和。

给定数组 \(a\) 满足 $ 1 \le a_1 \le a_2 \le \dots \le a_n \le 100 $ 。 你可以任意调整元素的顺序,也可以不调整,使它变成一个美的数组。

题目分析:

我们可以考虑从大到小排序,这样除了最大值可能出问题,其它的都没问题。
而最大值就可以只保留一个在序列开头,其余的放到结尾即可。

代码:

点击查看代码
#include<bits/stdc++.h>
using namespace std;
const int N = 100;
int a[N];
int main(){
	int T;scanf("%d",&T);
	while(T--){
		int n;scanf("%d",&n);
		for(int i=1; i<=n; i++)	scanf("%d",&a[i]);
		vector<int> v;
		for(int i=n; i>=1; i--){
			if(a[i] == a[n] && i != n)	continue;
			v.push_back(a[i]);
		}
		for(int i=n; i>=1; i--){
			if(a[i] == a[n] && i != n)	v.push_back(a[i]);
		}
		bool flag = true;
		int sum = 0;
		for(int i=0; i<(int)v.size(); i++){
			if(v[i] == sum)	flag = false;
			sum += v[i];
		}
		if(flag){
			printf("YES\n");
			for(int i=0; i<(int)v.size(); i++)	printf("%d ",v[i]);
			printf("\n");
		}
		else	printf("NO\n");
	}
	return 0;
}

B.Matrix of Differences

题目描述:

对于一个 \(n\times n\) 的矩阵,对于每一对相邻(有公共边)的值 \(a,b\),写下 \(|a-b|\)(即 \(a\) 与 \(b\) 差的绝对值)。定义这个矩阵的美丽度为写下的不同的值的个数。以如下的矩阵为例:

\[\left(\begin{matrix}1&3\\4&2\end{matrix}\right) \]

则所有相邻值的绝对值分别是 \(|1-3|=2,|1-4|=3,|3-2|=1,|4-2|=2\)。共有 \(1,2,3\) 三种不同的值,则这个矩阵的美丽度为 \(3\)。

给你 \(t\) 次询问,每次询问给定一个正整数 \(n\)。输出任意一个 \(n\times n\) 的矩阵,满足 \(1\sim n^2\) 在矩阵中各出现一遍,并且该矩阵的美丽度最大。

\(1\le t\le49,2\le n\le50\)。

题目分析:

手摸了半天才搞出来的做法。
考虑 \(n-1\) 会怎么得到,只有可能是 \(1,n\),那 \(n-2\) 呢?
可以是 \(1,n-1\) 或者 \(2,n\),为了构造的漂亮程度我们就不妨将 \(n,n-1\) 放到 \(1\) 的旁边,然后 \(2\) 继续放到下面,也就是下面这种方式:

\[\begin{matrix} 1 &n-1 &4 &n-5 &\cdots\\ n &3 &n-4 &\cdots\\ 2 &n-3 &\cdots\\ n-2 &\cdots\\ \cdots \end{matrix} \]

然后就可以过了。

代码:

点击查看代码
#include<bits/stdc++.h>
using namespace std;
const int N = 100;
int a[N][N];
int main(){
//	freopen("in.txt","r",stdin);
//	freopen("out.txt","w",stdout);
	int T;scanf("%d",&T);
	while(T--){
		int n;scanf("%d",&n);
		int l = 1,r = n*n,tot = 0;
		for(int i=1; i<=n; i++){
			++tot;
			int nx = i,ny = 1;
			while(nx >= 1 && ny <= n){
				if(tot & 1)	a[nx][ny] = l,l++;
				else a[nx][ny] = r,--r;
				nx --,ny ++;
			}
		}
		for(int i=2; i<=n; i++){
			++tot;
			int nx = n,ny = i;
			while(nx >= 1 && ny <= n){
				if(tot & 1)	a[nx][ny] = l,l++;
				else a[nx][ny] = r,--r;
				nx --,ny ++;
			}
		}
		for(int i=1; i<=n; i++){
			for(int j=1; j<=n; j++){
				printf("%d ",a[i][j]);
			}
			printf("\n");
		}
	}
	return 0;
}

C.Yet Another Tournament

题目描述:

有 \(n\) 个选手,编号为 \(1\) 至 \(n\) ,每两个选手对战时,编号大的将会胜利。

你可以准备 \(m\) 单位时间,每准备 \(a_i\) 时间就可以赢 \(i\) 号选手。

按胜利的总次数排名,求你最高多少名。

题目分析:

一个想法就是我们直接将 \(a\) 最小到大排序,这样就可以赢尽可能多的场,看上去就是很好的排名。
但是我们的排名还与我们赢了哪些人有关,所以就有点不可做的样子。
注意到,当我们赢了 \(x\) 场就相当于要选择 \(n-x\) 个人多赢一场,然后寻找赢场数大于 \(x\) 的人的个数,而只有赢场数等于 \(x\) 的人会受到我们选择加一的影响,所以其实此时只需要判断能不能通过调整使得我们可以赢过胜场为 \(x\) 的人即可。

代码:

点击查看代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N = 5e5+5;
int a[N],b[N];
signed main(){
//	freopen("in.txt","r",stdin);
//	freopen("out.txt","w",stdout);
	int T;scanf("%lld",&T);
	while(T--){
		int n,m;scanf("%lld%lld",&n,&m);
		for(int i=1; i<=n; i++)	scanf("%lld",&a[i]),b[i] = a[i];
		sort(b+1,b+n+1);
		int pos = 0;
		for(int i=1; i<=n; i++){
			if(m - b[i] >= 0){
				pos = i;
				m -= b[i];
			}
		}
		if(!pos){
			printf("%lld\n",n+1);
			continue;
		}
		if(pos != n && a[pos+1] <= m + b[pos])	++pos;
		printf("%lld\n",n-pos + 1);
	}
	return 0;
}

D.Different Arrays

题目描述:

给你一个有 \(n\) 个元素的序列,你需要进行 \(n-2\) 次操作。

对于第 \(i\) 次操作,你可以选择让 \(a_i-a_{i+1}\) 且 \(a_{i+2}+a_{i+1}\) 或者可以选择让 \(a_i+a_{i+1}\) 且 \(a_{i+2}-a_{i+1}\)

问最后能产生多少个不同的序列。

题目分析:

一个想法就是判断什么样的序列是能被表示的,但是想了一会发现根本没有任何头绪,所以考虑换个想法,也就是直接使用 \(dp\) 去决策每一次的操作。
为了方便理解,我们将第 \(i\) 次操作,成为操作第 \(i+1\) 个数。
但是这样看上去有很多重复的情况就很难办,注意一点就是要使得产生相同的序列则必然满足存在 \(a_i = 0\) 的情况,然后操作 \(a_i\),否则一定不会产生相同的情况,所以我们完全不用考虑什么去重之类的问题,只需要判断 \(a_i = 0\) 即可。
所以可以考虑设 \(dp_{i,j}\) 表示操作完了前 \(i\) 个数,\(a_{i+1} = j\) 的方案数,记第二维的原因是我们此时需要决策第 \(i+1\) 次操作就必须知道对应的 \(a\) 是什么。
转移就是显然的,也就是直接枚举 \(a_{i+1}\) 是怎么操作的,以及特判 \(a_{i+1} = 0\)。
注意到第二维可以为负,所以加一个偏移量。

代码:

点击查看代码
#include<bits/stdc++.h>
using namespace std;
const int MOD = 998244353;
const int MAX = 90000;
int dp[305][90005 * 2];
int a[305];
void add(int &a,int b){
	a = (a + b)%MOD;
}
int main(){
	int n;scanf("%d",&n);
	for(int i=1; i<=n; i++)	scanf("%d",&a[i]);
	dp[1][MAX+a[2]] = 1;
	for(int i=1; i<n; i++){
		for(int k=-MAX; k<=MAX; k++){
			if(!dp[i][k+MAX])	continue;
			if(k == 0){
				dp[i+1][a[i+2]+MAX] = (dp[i+1][a[i+2]+MAX] + dp[i][k+MAX])%MOD;
			}
			else{
				dp[i+1][a[i+2]+k+MAX] = (dp[i+1][a[i+2]+k+MAX] + dp[i][k+MAX])%MOD;
				dp[i+1][a[i+2]-k+MAX] = (dp[i+1][a[i+2]-k+MAX] + dp[i][k+MAX])%MOD;
			}
		}
	}
	int ans = 0;
	for(int j=-MAX; j<=MAX; j++)	add(ans,dp[n-1][j+MAX]);
	printf("%d\n",ans);
	return 0;
}

E.Game of the Year

题目描述:

Monocarp 和 Polycarp 正在玩电脑游戏。游戏特点:$ n $ 个编号从 $ 1 $ 到 $ n $ 的BOSS。

他俩将用以下方式与BOSS战斗

  • Monocarp 进行 $ k $ 次尝试撒掉boss;
  • Polycarp 进行 $ k $ 次尝试撒掉boss;
  • Monocarp 进行 $ k $ 次尝试撒掉boss;
  • Polycarp 进行 $ k $ 次尝试撒掉boss;
  • ...

Monocarp 在第 $ a_i $ 次尝试中撒掉了第 $ i $ 只BOSS。Polycarp 在第 $ b_i $ 次尝试中撒掉了第 $ i $ 只BOSS。其中一个人撒掉第 $ i $ 只BOSS后,他们就会尝试撒第 $ (i+1) $ 只BOSS。并且他们的尝试计数器都会清空。撒掉第 $ n $ 只BOSS后,游戏结束。

找到从$ 1 $ 到 $ n $所有的 $ k $ 值, 使得 Monocarp 可以杀死所有的BOSS。

\(1 \le n \le 2\times 10^5\)

题目分析:

题目说的实在是太抽象了,转化一下题意就是要找到满足以下条件的 \(k\):

\[\begin{aligned} &\forall i\in[1,n] & \lceil \frac{a_i}{k} \rceil \le \lceil \frac{b_i}{k} \rceil \end{aligned} \]

首先就是可以直接整除分块就能找到所有满足条件的 \(k\),复杂度 \(O(n\sqrt{n})\) 但是常数有点逆天据说不能过,考虑优化。
一个经典的想法就是既然不能枚举约数,那么我们就枚举倍数,即枚举 \(k\) 然后枚举 \(k\) 的倍数。
可以发现 \(k\) 的倍数将序列分成了 \(O(\frac{n}{k})\) 段,而要使得上述条件满足就是 \(a_i\) 所在的块不在 \(b_i\) 所在的块后面。
如果原来就满足 \(a_i \le b_i\) 则无论如何都满足条件,就不管了。
如果 \(a_i > b_i\) 条件其实就是 \(a_i\) 所在的块与 \(b_i\) 所在的块相同,这个不是很好判,那么什么时候是不在一块呢?
既然不在一块也就是说 \([b_i,a_i]\) 跨过了一个分界点,如果我们以 \(k\) 的倍数作为每一段的右端点,也就是 \([b_i,a_i)\) 包含 \(k\) 的倍数。
可以直接预处理出每一个位置是否可以作为右端点,然后对于每一个 \(k\) 的倍数判断一下即可。
复杂度 \(O(n \log n)\)

代码:

点击查看代码
#include<bits/stdc++.h>
using namespace std;
const int N = 5e5+5;
int a[N],b[N],sum[N];
vector<int> v;
int main(){
//	freopen("in.txt","r",stdin);
//	freopen("out.txt","w",stdout);
	int T;scanf("%d",&T);
	while(T--){
		int n;scanf("%d",&n);
		for(int i=1; i<=n; i++)	scanf("%d",&a[i]);
		for(int i=1; i<=n; i++)	scanf("%d",&b[i]);
		for(int i=1; i<=n; i++){
			if(a[i] > b[i])	sum[b[i]]++,sum[a[i]]--;
		}
		for(int i=1; i<=n; i++)	sum[i] += sum[i-1];
		for(int i=1; i<=n; i++){
			bool flag = true;
			for(int j=i; j<=n; j+=i){
				if(sum[j])	flag = false;
			}
			if(flag)	v.push_back(i);
		}
		printf("%d\n",v.size());
		for(int i=0; i<v.size(); i++)	printf("%d ",v[i]);
		printf("\n");
		for(int i=0; i<=n; i++)	sum[i] = 0;
		v.clear();
	}
	return 0;
}

F.Double Sort II

题目描述:

有两个 \(1..n\) 的排列 \(a,b\)。

你可以进行若干次操作,每次操作流程如下:

  • 选择一个整数 \(i \in [1,n]\)。

  • 找出两个整数 \(x,y\),使得 \(a_x=b_y=i\)。

  • 交换 \(a_x\) 和 \(a_i\),以及 \(b_y\) 和 \(b_i\)。

问把 \(a\) 和 \(b\) 从小到大排序的最小操作次数

题目分析:

考虑将排列看作一个置换,然后建图,也就是连边 \(i \to a_i\) 与 \(i \to b_i\),注意这是两张图。
我们的一次操作相当于将某个点缩成一个自环,其他点不受影响,所以对于每一个置换环设其长度为 \(len\) 只需要操作 \(len-1\) 次就可以将所有点缩成自环,即我们可以对于每一个环钦定一个点使得这个点不被操作,要最大化钦定点的数量。
而两张图其实也是差不多的,如果钦定 \(i\) 不被操作,也就是说 \(i\) 在 \(a,b\) 中的环上均只能选择 \(i\) 这一个点不被操作,这个其实就是一个匹配的感觉。
所以可以对于每一个点 \(i\),找到其在 \(a,b\) 上的环,将这两个环连边,最后跑一个最大匹配就是最多的不用被操作的点数。

代码:

点击查看代码
#include<bits/stdc++.h>
using namespace std;
const int N = 1e5+5;
struct edge{
	int nxt,to,id;
	edge(){}
	edge(int _nxt,int _to,int _id){
		nxt = _nxt,to = _to,id = _id;
	}
}e[2 * N];
int cnt,col1[N],col2[N],head[N],flag[N],match[N],a[N],b[N];
bool vis[N],tag[N];
void add_edge(int from,int to,int id){
	e[++cnt] = edge(head[from],to,id);
	head[from] = cnt;
}
bool dfs(int now){
	if(vis[now])	return false;
	vis[now] = true;
	for(int i=head[now] ;i ; i=e[i].nxt){
		int to = e[i].to;
		if(!match[to] || dfs(match[to])){
			match[now] = to,match[to] = now;
			flag[now] = flag[to] = e[i].id;
			return true;
		}
	}
	return false;
}
void dfs1(int now,int col){
	if(col1[now])	return;
	col1[now] = col;
	if(!col1[a[now]])	dfs1(a[now],col);
}
void dfs2(int now,int col){
	if(col2[now])	return;
	col2[now] = col;
	if(!col2[b[now]])	dfs2(b[now],col);
}
int main(){
//	freopen("in.txt","r",stdin);
//	freopen("out.txt","w",stdout);
	int n;scanf("%d",&n);
	for(int i=1; i<=n; i++)	scanf("%d",&a[i]);
	for(int i=1; i<=n; i++)	scanf("%d",&b[i]);
	int tot = 0;
	for(int i=1; i<=n; i++){
		if(col1[i])	continue;
		++tot;dfs1(i,tot);
	}
	int tmp = tot;
	memset(vis,false,sizeof(vis));
	for(int i=1; i<=n; i++){
		if(col2[i])	continue;
		++tot;dfs2(i,tot);
	}
	for(int i=1; i<=n; i++){
		add_edge(col1[i],col2[i],i);
		add_edge(col2[i],col1[i],i);
//		printf("%d %d\n",col1[i],col2[i]);
	}
	int ans = 0;
	for(int i=1; i<=tmp; i++){
		memset(vis,false,sizeof(vis));
		ans += dfs(i);
	}
	printf("%d\n",n - ans);
	for(int i=1; i<=tmp; i++){
		tag[flag[i]] = true;
	}
	for(int i=1; i<=n; i++){
		if(!tag[i])	printf("%d ",i);
	}
	return 0;
}

G.Weighed Tree Radius

题目描述:

给你一个\(n\)个点的树和\(n-1\)条边。第\(i\)个点的初始权值为\(a_i\)。

定义结点\(v\)到结点\(u\)的距离\(d_v(u)\)等于\(v\)和\(u\)之间的边的数量。注意:\(d_v(u)=d_u(v),d_v(v)=0\)

定义结点\(v\)到结点\(u\)的权值距离\(w_v(u)=d_v(u)+a_u\)。注意:\(w_v(v)=a_v,w_v(u) \neq w_u(v)\)如果\(a_u \neq a_v\)

与通常的距离类似,让我们定义结点\(v\)的偏心距\(e(v)\)是从\(v\)到其他结点的最大权值距离(包括\(v\)本身),即\(e(v)=\max\limits_{1\leq u \leq n} w_v(u)\)。

最后,我们定义树的半径\(r\)是所有偏心距的最小值,即\(r=\min\limits_{1\leq v\leq n} e(v)\)

你需要对\(m\)次询问进行回答,对于第\(j\)次询问,给出两个数\(v_j\)和\(x_j\),表示将\(a_{v_j}\)的值修改为\(x_j\)。

在每次询问后,输出当前该树的半径\(r\)。

\(2 \le n \le 2 \times 10^5,1\le m \le 10^5\)

题目分析:

题目已经提示了这东西叫做半径,那么是不是直接求直径然后除以 \(2\) 就可以呢?
我们定义 \(w'(u,v) = a_u + a_v + dis(u,v)\),那么满足 \(w'(u,v)\) 最大的两个点 \(u,v\) 之间的路径的长度我们称为直径。
这里我们将 \(a_u\) 理解为挂在 \(u\) 上长度为 \(a_u\) 的链,\(a_v\) 理解为挂在 \(v\) 上长度为 \(a_v\) 的链。
设直径的中点为 \(mid\),若 \(mid\) 在直径的某一个节点上,则显然 \(r = \lceil \frac{w'(u,v)}{2} \rceil\),可是如果 \(mid\) 不在直径的某一个节点上呢。
若 \(mid\) 在 \(a_u\) 对应的链上,则必然满足 \(e_u = a_u\),则对于其它的任意一个点 \(x\) 都必然满足 \(e_x \ge dis(u,x) + a_u > a_u = e_u\),即 \(r = a_u\),但是这样的话就必然满足直径为 \(w'(u,u)\) 就不可能不存在了。

下面我们的问题就转化为了维护直径。
考虑假设我们原来的直径为 \((u,v)\) 现在将 \(a_x\) 增大了一些,那么我们新直径的端点必然是 \(u,v,x\) 其中的两个,可以直接分类讨论得到答案。
而如果我们将 \(a_x\) 减小了一些,我们就无法判断直径端点的变化,所以可以考虑使用线段树分治维护修改,这样每次将值从 \(0\) 开始变化,这样每次都是加的操作了。

标签:Educational,le,题目,CF1783,int,题解,scanf,操作,now
From: https://www.cnblogs.com/linyihdfj/p/17698589.html

相关文章

  • 【题解】DP选练(23.9.11 - 23.9.12)
    一些写过题解的题我就直接挂连接了。[NOIP2018提高组]货币系统题目描述:在网友的国度中共有\(n\)种不同面额的货币,第\(i\)种货币的面额为\(a[i]\),你可以假设每一种货币都有无穷多张。为了方便,我们把货币种数为\(n\)、面额数组为\(a[1..n]\)的货币系统记作\((n,a)\)。......
  • [题解]AT_arc116_b [ARC116B] Products of Min-Max
    思路我们容易可以得到一个朴素的做法,首先对\(a\)数组排序,然后枚举最大值和最小值\(a_i,a_j\),那么对于中间的元素都有选与不选两种情况,得到答案:\[\sum_{i=1}^{n}(a_i\timesa_i+(\sum_{j=i+1}^{n}a_i\timesa_j\times2^{j-i-1}))\]然后对这个式子做一个化简:......
  • 【Flink系列十八】HDFS_DELEGATION_TOKEN过期的问题解决汇总
    问题类别Spark框架自身的问题Hadoop全家桶的问题开发者通过Hive,HDFS,HBASE访问HDFS的问题排查已知Hadoop-common-2.6.0的UGI存在bug,代码为HADOOP-10786,该问题在CDH发行版中已经修复,但Apache版本存在问题。已知HDFS也存在一个HDFS_DELEGATION_TOKEN过期的bug,代码为HDFS-9......
  • xilinx赛灵思下载器jtag-hs3兼容alinx仿真fpga烧录digilent高速常见问题解答
    1.概述  XJTAG-HS3是XILINX的USB转JTAG的高速仿真器,可以下载、烧录和仿真Xilinx FPGA和CPLD芯片,以及配置PROM、FLASH. XJTAG-HS3比PlatformCableUSBII下载器快10倍速度。 可以在30Mbit/秒下驱动JTAG/SPI总线,并且能实现对XilinxZYNQ平台处理器核的重置。可以支持ZYN......
  • P3616 富金森林公园 题解
    P3616富金森林公园题解题意给你\(n\)个点,有\(m\)次操作,每次操作可以改变一个数的值,也可以查询有多少连续的块,满足这个块内的所有数的值都大于查询的值。分析还是比较容易想到用数据结构或分块的,毕竟有同时存在修改和查询操作。但是维护什么?怎么维护?既然我们无法直接维......
  • 关于sql server 2008 r2 安装闪退问题解决办法
    打开sqlserverr2安装包文件目录找到SQL2008R2_64\2052_chs_lp\x64\setup\sqlsupport_msi目录下sqlsupport.msi,运行安装 a、在安装盘中搜索sqlsupport,找到对应的sqlsupport.msi文件并安装,一般路径如下:Windows64位系统需要安装:..\sql2008r2.iso\2052_chs_lp\x64\setup\sqls......
  • 【ABC105D】题解
    题解题意简述给定\(n\)个数,求这\(n\)个数中有多少个二元组\((x,y)\)满足其中每一个数都是\(m\)的倍数。思路前缀和,\((x,y)\)内每一个数\(\bmod\m=0\),可以用\((sum_y-sum_{x-1})\bmod\m=0\)表示。但是这题数据太大,所以要使用map。如果\(x=1\),......
  • ABC319 A-E 题解
    A用map<string,int>将名字对应的值存下来即可。赛时代码B按照题意暴力模拟,注意细节。赛时代码C答辩题,卡了我半个小时。枚举\(1\sim9\)的全排列,然后按照顺序计算即可,但代码实现比较答辩。赛时代码D显然具有可二分性,直接二分并判定可行性即可,注意不合法条件。赛......
  • 题解 Gym 104531D【Coffee】
    2022SYSUSchoolContest题目不想翻译了,自己看能看懂。problamThegirlsofHTTlikedrinkingtea.Butoneday,theywantedachangeanddecidedtotrycoffeeinthenext\(n\)days.NowMugi,whoalwaysprovidesfoodanddrinksforHTT,willgototheshopto......
  • pycharm 远程debug卡住问题解决
     解决方案:1、先注释掉连接debugserversocket代码,启动 2、启动debugserver3、去除注释,热部署自动重启,则能重连 ......