评价: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 Codeforces Round 141(CF1783)的更多相关文章

  1. Educational Codeforces Round 141 解题报告

    Educational Codeforces Round 141 解题报告 \(\text{By DaiRuiChen007}\) \(\text{Contest Link}\) A. Make it ...

  2. Educational Codeforces Round 141 (Rated for Div. 2) A-E

    比赛链接 A 题意 给一个数组 \(a\) ,要求重排列以后 \(a[i] \neq a[1,i-1]\) ,其中 \(a[1,i-1]\) 是前 \(i-1\) 项和. 如果无解则输出 NO :否则 ...

  3. Educational Codeforces Round 63 (Rated for Div. 2) 题解

    Educational Codeforces Round 63 (Rated for Div. 2)题解 题目链接 A. Reverse a Substring 给出一个字符串,现在可以对这个字符串进 ...

  4. Educational Codeforces Round 48 (Rated for Div. 2) CD题解

    Educational Codeforces Round 48 (Rated for Div. 2) C. Vasya And The Mushrooms 题目链接:https://codeforce ...

  5. Educational Codeforces Round 60 (Rated for Div. 2) 题解

    Educational Codeforces Round 60 (Rated for Div. 2) 题目链接:https://codeforces.com/contest/1117 A. Best ...

  6. Educational Codeforces Round 59 (Rated for Div. 2) DE题解

    Educational Codeforces Round 59 (Rated for Div. 2) D. Compression 题目链接:https://codeforces.com/contes ...

  7. Educational Codeforces Round 58 (Rated for Div. 2) 题解

    Educational Codeforces Round 58 (Rated for Div. 2)  题目总链接:https://codeforces.com/contest/1101 A. Min ...

  8. Educational Codeforces Round 65 (Rated for Div. 2)题解

    Educational Codeforces Round 65 (Rated for Div. 2)题解 题目链接 A. Telephone Number 水题,代码如下: Code #include ...

  9. Educational Codeforces Round 64 (Rated for Div. 2)题解

    Educational Codeforces Round 64 (Rated for Div. 2)题解 题目链接 A. Inscribed Figures 水题,但是坑了很多人.需要注意以下就是正方 ...

  10. Educational Codeforces Round 64 部分题解

    Educational Codeforces Round 64 部分题解 不更了不更了 CF1156D 0-1-Tree 有一棵树,边权都是0或1.定义点对\(x,y(x\neq y)\)合法当且仅当 ...

随机推荐

  1. Java(instanceof和类型转换)

    1.instanceof和类型转换 instanceof 引用类型比较,判断一个对象是什么类型 public static void main(String[] args) { // Object & ...

  2. C++面试八股文:C和C++有哪些区别?

    某日小二参加XXX科技公司的C++高级工程师开发岗位1面: 面试官:请问C和C++的区别有哪些? 小二:C++是C的超集. 面试官:还有吗? 小二:... 面试官:面试结束,回去等消息吧. 小二:淦. ...

  3. 公路堵车概率模型Python(Nagel-Schreckenberg交通流模型)

    路面上有N辆车,以不同速度向前行驶,模拟堵车问题.有以下假设: 假设某辆车的当前速度是 v 如果 前方可见范围内没车,下一秒车速提高到 v+1 如果 前方有车,前车的距离为 d ,且 d < v ...

  4. CSR格式如何更新? GES图计算引擎HyG揭秘之数据更新

    摘要:HyG图计算引擎采用CSR格式来存储图的拓扑信息,CSR格式可以将稀疏矩阵的存储空间压缩,进而大大降低图的存储开销,同时具备访问效率高.格式易转化等优点. 本文分享自华为云社区<CSR格式 ...

  5. 前端vue 宫格组件提供常见九宫格菜单组件,扩充性好,可切换九宫格 十二宫格 十五宫格

    快速实现vue uni-app宫格组件提供常见九宫格菜单组件,扩充性好,可切换九宫格 十二宫格 十五宫格; 下载完整代码请访问uni-app插件市场地址:https://ext.dcloud.net. ...

  6. 第二章 VM与Linux的安装

    1. VMWare 安装 ​​ ‍ ​​ ‍ ‍ 2. CentOS 安装 ‍ 安装网址:https://blog.csdn.net/qq_41819965/article/details/11118 ...

  7. 记一次etcd全局锁使用不当导致的事故

    1.背景介绍 前两天,现场的同事使用开发的程序测试时,发现日志中报etcdserver: mvcc: database space exceeded,导致 etcd 无法连接.很奇怪,我们开发的程序只 ...

  8. 图像处理_ISP_坏点矫正

    1 坏点介绍 图像坏点(Bad pixel) : 图像传感器上光线采集点(像素点)所形成的阵列存在工艺上的缺陷,或光信号进行转化为电信号的过程中出现错误,从而会造成图像上像素信息错误,导致图像中的像素 ...

  9. 用 Golang 从0到1实现一个高性能的 Worker Pool(一) - 每天5分钟玩转 GPT 编程系列(3)

    目录 1. 概述 2. 设计 2.1 让 GPT-4 给出功能点 2.2 自己总结需求,再给 GPT 派活 3. 实现 3.1 你先随意发挥 3.2 你得让 Worker 跑起来呀 3.3 你说说 P ...

  10. GaussDB技术解读丨高级压缩

    本文作者|华为云数据库GaussDB首席架构师 冯柯 [背景介绍] 数据压缩与关系数据库的结合,早已不是一个新鲜的话题,当前我们已经看到了各种各样数据库压缩的产品和解决方案.对于GaussDB来说,在 ...