评价: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. 【python基础】复杂数据类型-列表类型(排序/长度/遍历)

    1.列表数据元素排序 在创建的列表中,数据元素的排列顺序常常是无法预测的.这虽然在大多数情况下都是不可避免的,但经常需要以特定的顺序呈现信息.有时候希望保留列表数据元素最初的排列顺序,而有时候又需要调 ...

  2. java匿名内部类的初解

    java原生态中的匿名内部类 1.匿名内部类的定义 使用匿名内部类的两种的方法 建立父类,重写父类的方法 实现接口的方法 2.普通类的实现 1. 普通类实现 实现普通类需要先声明对一个类的对象,再调用 ...

  3. 前端学习C语言 - 函数和关键字

    函数和关键字 本篇主要介绍:自定义函数.宏函数.字符串处理函数和关键字. 自定义函数 基本用法 实现一个 add() 函数.请看示例: #include <stdio.h> // 自定义函 ...

  4. chatgpt入口,免费在线chatgpt--与人工智能聊天?尝试chatgpt入口,免费在线chatgpt吧!

    介绍一款人工智能聊天机器人--chatgpt入口 chatgpt是一款智能聊天机器人,它能够与人类进行自然语言对话,可以回答问题.提供建议,还可以玩游戏和聊天互动,是当前最受欢迎的人工智能聊天工具之一 ...

  5. 洛谷 P4859 已经没有什么好害怕的了

    题目描述 学姐 4 了. 有 \(n\) 个糖果和 \(n\) 个药片,它们要进行一一配对.每个糖果或药片都具有互不相同的能量值,要求配对后,糖果比药片能量高的对数,比剩下的对数恰好多 \(k\),求 ...

  6. 【技术积累】Vue.js中的基础概念与语法【一】

    写在前面 学习Vue之前最好有前端三驾马车的基础[HTML+CSS+JavaScript] 笔者接了一个从头开发的Vue项目,由于公司急着要,没有时间慢慢像在学校里学了,只能边学边做,现在项目雏形已经 ...

  7. React后台管理系统11 配置项目初始化展开代码

    在上一文中,我们已经配置好了,刷新默认打开选中的样式,但是如果是在/page3/1,这种的,并没有选中到/page3里面的/page3/1,这个地方来,所以我们需要解决的就是这几个问题: 思路如下: ...

  8. Lamada List 去重及其它操作示例

    import java.util.concurrent.ConcurrentHashMap; import java.util.function.Function; import java.util. ...

  9. 倒排Tree树

    倒排Tree树 需求说明为: ​ 树节点存在(标识)或者叶子节点存在标识 都需要展示出来 存在※的节点及其上级节点需要返回 其余节点需要剔除 ​ A() ----------------------- ...

  10. python学习笔记:第七章面向对象

    与java类似,python作为一种面向对象的编程语言,也可以创建自定义的对象和类. 它的特性主要有:继承,封装,多态,方法,属性,超类 1.变量的作用域 c = 50 #全局变量, 作用域为整个模块 ...