评价: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. Rust 声明式宏中的 Metavariables 有哪些

    Metavariables 官方文档确实写得很好,但是缺少一些风味,容易催眠‍ 还是直接看例子更爽一些,通常我们可以从示例代码中之间看出官方文档要表达的意思,而且很多时候我们可以直接在示例代码的基础上 ...

  2. 驱动开发:内核RIP劫持实现DLL注入

    本章将探索内核级DLL模块注入实现原理,DLL模块注入在应用层中通常会使用CreateRemoteThread直接开启远程线程执行即可,驱动级别的注入有多种实现原理,而其中最简单的一种实现方式则是通过 ...

  3. Airtest图像识别测试工具原理解读&最佳实践

    1 Airtest简介 Airtest是一个跨平台的.基于图像识别的UI自动化测试框架,适用于游戏和App,支持平台有Windows.Android和iOS.Airtest框架基于一种图形脚本语言Si ...

  4. 4. SpringMVC获取请求参数

    1. 通过 ServletAPI 获取 ‍ 将 HttpServletRequest 作为控制器方法的形参 , 此时 HttpServletRequest 类型的参数表示封装了当前请求的请求报文的对象 ...

  5. 自然语言处理(NaturalLanguageProcessing,NLP)领域的100篇热门博客文章标题如下:

    目录 文章标题:<自然语言处理(Natural Language Processing,NLP)领域的100篇热门博客文章标题如下> 背景介绍: 随着人工智能技术的不断发展和普及,自然语言 ...

  6. tSNE算法在自然语言处理中的应用:文本降维和可视化

    目录 技术原理及概念 t-SNE(Toeplitz-Stochastic Neural Network)是一种常用的文本降维和可视化算法,它的核心思想是将高维文本数据映射到低维空间,同时保持数据的一致 ...

  7. ModifyAjaxResponse,修改ajax请求返回值,前后端调试之利器

    一.概要 先看图 京豆多的离谱,你的第一想法肯定是:按F12修改了网页元素 没那么简单,你看支持刷新的 肯定还是假的,通过 Fiddler 或 Wireshark 等抓包工具修改了响应包:或者干脆改了 ...

  8. 即构✖叮咚课堂:行业第一套AI课堂解决方案是怎么被实现的?

    AI走进教育,是传统教育的一次迭代进化 在教育问题上,我们看到两类话题最容易引发公众讨论:教育公平和个性化教育,"互联网+教育"有可能解决第一类话题,"AI教育" ...

  9. RDB初步了解

    RDB概念 快照文件是.rdb结尾 redis6.2以前和之后(包括6.2)在什么条件下会保存rdb文件有所不同 以前 15min&&1个key(改变)调用保存 5min&&a ...

  10. 基准测试工具 --- BenchmarkDotNet

    介绍 今天介绍一个非常强大的基于.Net 的基准测试工具BenchmarkDotNet. BenchmarkDotNet 已经被14300多个项目采用,包括非常多的知名开源项目,例如 dotnet/p ...