Educational Codeforces Round 141 解题报告

\(\text{By DaiRuiChen007}\)

\(\text{Contest Link}\)

A. Make it Beautiful

所有 \(\{a_i\}\) 相等显然不行,把最大的 \(a_i\) 放最前面,只要第二个和第一个不同就可以随便排了

我这里是用出现次数进行分层,对于出现次数 \(\ge 1,\ge 2,\ge 3,\cdots\) 的值分别逆序排列

时间复杂度 \(\Theta(n\log n)\),瓶颈在 map 统计出现次数上

#include<bits/stdc++.h>
using namespace std;
const int MAXN=101;
map <int,int> cnt;
vector <int> a,p[MAXN];
inline void solve() {
cnt.clear(),a.clear();
int n;
scanf("%d",&n);
a.resize(n);
for(int i=1;i<=n;++i) p[i].clear();
for(int i=0;i<n;++i) {
scanf("%d",&a[i]);
++cnt[a[i]];
p[cnt[a[i]]].push_back(a[i]);
}
if(cnt.size()==1) puts("NO");
else {
puts("YES");
for(int i=1;i<=n;++i) {
sort(p[i].begin(),p[i].end(),greater<int>());
for(int j:p[i]) printf("%d ",j);
}
puts("");
}
}
signed main() {
int T;
scanf("%d",&T);
while(T--) solve();
return 0;
}

B. Matrix of Differences

大胆猜测答案为 \(n^2-1\) 即 \(1\sim n^2\) 都有

先考虑一行的情况,即把 \(1\sim n\) 重排成一个序列,使得相邻两个数的差恰好出现 \(1\sim n-1\)

简单模拟即可得到一个合法的序列:\(\{1,n,2,n-1,3,n-2,\cdots\}\)

因此构造一个长度为 \(n^2\) 的序列然后加几个拐点塞进 \(n\times n\) 的矩阵里即可

时间复杂度 \(\Theta(n^2)\)

#include<bits/stdc++.h>
using namespace std;
const int MAXN=51;
int a[MAXN][MAXN];
inline void solve() {
int n;
scanf("%d",&n);
int L=1,R=n*n;
for(int i=1;i<=n;++i) {
if(i%2==1) {
for(int j=1;j<=n;++j) {
a[i][j]=(i+j)%2==1?R--:L++;
}
} else {
for(int j=n;j>=1;--j) {
a[i][j]=(i+j)%2==1?R--:L++;
}
}
}
for(int i=1;i<=n;++i) {
for(int j=1;j<=n;++j) {
printf("%d ",a[i][j]);
}
puts("");
}
}
signed main() {
int T;
scanf("%d",&T);
while(T--) solve();
return 0;
}

C. Yet Another Tournament

显然首先应该击败尽可能多的人,这一步可以按 \(a_i\) 小到大贪心

然后假设你已经确定你最多击败 \(k\) 个人,那么第 \(1\sim k\) 个人获胜次数一定不超过 \(k\),第 \(k+2\sim n\) 个人获胜次数一定超过 \(k\),而第 \(k+1\) 个人获胜次数为 \(k\) 或 \(k+1\),假如你能在不影响获胜次数的前提下击败 \(k+1\),你的排名会更高

因此你只需要优先击败 \(k+1\),然后判断剩下的 \(m\) 够不够你再击败 \(k-1\) 个人即可

时间复杂度 \(\Theta(n\log n)\),瓶颈在对 \(\{a_i\}\) 排序上

#include<bits/stdc++.h>
#define int long long
#define pii pair<int,int>
using namespace std;
const int MAXN=5e5+1;
int n,m,a[MAXN];
inline int calc(const vector<pii> &p) {
int lim=m;
for(int i=0;i<n;++i) {
if(p[i].first>lim) return i;
lim-=p[i].first;
}
return n;
}
inline void solve() {
scanf("%lld%lld",&n,&m);
vector <pii> p;
for(int i=1;i<=n;++i) scanf("%lld",&a[i]),p.push_back(make_pair(a[i],i));
sort(p.begin(),p.end());
int ans=calc(p);
if(ans==n) printf("1\n");
else if(ans==0) printf("%lld\n",n+1);
else {
for(int i=0;i<n;++i) {
if(p[i].second==ans+1) {
auto del=p[i];
p.erase(p.begin()+i);
p.insert(p.begin(),del);
}
}
if(calc(p)==ans) printf("%lld\n",n-ans);
else printf("%lld\n",n-ans+1);
}
}
signed main() {
int T;
scanf("%lld",&T);
while(T--) solve();
return 0;
}

D. Different Arrays

先考虑什么时候两个不同的操作序列 \(X,Y\) 能够得到相同的 \(a\)

假设 \(j\) 是第一个 \(x_j\ne y_j\) 的位置,那么此时 \(X,Y\) 对应的序列中 \(a_j\) 分别变成 \(a_j-a_{j+1}\) 和 \(a_j+a_{j+1}\),又因为往后的操作不会改变 \(a_j\) 的值,因此只有可能在 \(a_{j+1}=0\) 时会产生重复

因此我们考虑 \(dp_{i,x}\) 表示进行了第 \(i\) 次操作前,\(a_{i+1}=x\) 的方案数,得到如下转移:

\[\begin{aligned}
dp_{i+1,a_{i+1}+x}&\gets dp_{i,x}\\
dp_{i+1,a_{i+1}-x}&\gets dp_{i,x}
\end{aligned}
\]

\(x=0\) 的时候产生的两个转移是重复的,只需要去掉其中一条转移即可,最终答案为 \(\sum_x dp_{n-1,x}\)

设 \(w\) 为 \(\{a_i\}\) 的值域,那么最终 \(x\) 的值域为 \(nw\),朴素转移即可

时间复杂度 \(\Theta(n^2w)\)

#include<bits/stdc++.h>
#define int long long
#define pii pair<int,int>
using namespace std;
const int MAXN=301,W=1e5+1,MOD=998244353;
int a[MAXN],dp[MAXN][W<<1];
//a[i+1]=j after process (i-1,i,i+1)
signed main() {
int n,ans=0;
scanf("%lld",&n);
for(int i=1;i<=n;++i) scanf("%lld",&a[i]);
dp[1][a[2]+W]=1;
for(int i=2;i<n;++i) {
for(int x=-W;x<W;++x) {
dp[i][a[i+1]+x+W]=(dp[i][a[i+1]+x+W]+dp[i-1][x+W])%MOD;
if(x!=0) dp[i][a[i+1]-x+W]=(dp[i][a[i+1]-x+W]+dp[i-1][x+W])%MOD;
}
}
for(int x=0;x<2*W;++x) ans=(ans+dp[n-1][x])%MOD;
printf("%lld\n",ans);
return 0;
}

E. Game of the Year

转化题意,得到题目实际是让你求满足 \(\forall i\in[1,n]:\left\lceil\dfrac{a_i}k\right\rceil\le \left\lceil\dfrac{b_i}k\right\rceil\) 的 \(k\) 的数量

容易想到用整除分块对于每个 \((a_i,b_i)\) 进行二维数论分块计算 \(k\) 合法的区间,但是 \(\Theta(n\sqrt n)\) 的复杂度容易被卡常,事实上还有复杂度更加优秀的算法

转化一下 \(\left\lceil\dfrac{a_i}k\right\rceil\le \left\lceil\dfrac{b_i}k\right\rceil\),显然对于 \(a_i\le b_i\),这样的 \(k\) 恒成立,因此只考虑 \(a_i>b_i\) 的情况,就等价于不存在 \(t\) 使得 \(b_i\le tk<a_i\),因此用差分对每个区间 \([a_i,b_i)\) 进行覆盖,如果 \(k\) 的某个倍数被覆盖那么 \(k\) 不合法

时间复杂度 \(\Theta(n\ln n)\),瓶颈在枚举 \(k\) 和 \(k\) 的所有倍数上

#include<bits/stdc++.h>
using namespace std;
const int MAXN=2e5+1;
int a[MAXN],b[MAXN],d[MAXN];
inline void solve() {
int n;
vector <int> ans;
scanf("%d",&n);
for(int i=1;i<=n;++i) d[i]=0;
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]) ++d[b[i]],--d[a[i]];
for(int i=1;i<=n;++i) d[i]+=d[i-1];
for(int i=1;i<=n;++i) {
bool ok=true;
for(int j=i;j<=n;j+=i) {
if(d[j]) {
ok=false;
break;
}
}
if(ok) ans.push_back(i);
}
printf("%d\n",(int)ans.size());
for(int i:ans) printf("%d ",i);
puts("");
}
signed main() {
int T;
scanf("%d",&T);
while(T--) solve();
return 0;
}

F. Double Sort II

先考虑只对 \(a\) 排序的最小花费,显然连接 \(i\to a_i\) 建图,对于图中的每一个大小为 \(|\mathbf C|\) 的置换环,根据复原置换的结论,我们需要 \(|\mathbf C|-1\) 次操作把这个环复原,设 \(cyc\) 为图中环的数量,答案为 \(n-cyc\),我们可以理解为每个在每个环中选出一个 \(x\) 不操作

接下来考虑同时对 \(a\) 和 \(b\) 排序,类似上面,对于 \(a,b\) 中的每个环,分别选出一个 \(x\) 不操作,我们称 \(x\) 为这个环的“代表”

注意到只有 \(x\) 同时是 \(a\) 中所属环和 \(b\) 中所属环的“代表”,我们才可以不操作 \(x\)

因此把 \(a\) 中的每个环看成左部点,\(v\) 中的每个环看成其右部点,每个位置 \(i\) 看成一条连接 \(i\) 在 \(a\) 中所属环和在 \(b\) 中所属环的一条边,得到一张二分图,二分图的每条匹配边对应选择相应的 \(x\) 作为其在两边所在的环的“代表”然后跳过对 \(x\) 的操作

设其最大匹配大小为 \(|\mathbf M|\),那么答案为 \(n-|\mathbf M|\),求出匹配方案输出即可

时间复杂度 \(\Theta(n^2)\)

#include<bits/stdc++.h>
using namespace std;
const int MAXN=3001;
vector <int> G[MAXN];
int a[MAXN],b[MAXN],ia[MAXN],ib[MAXN],tar[MAXN];
bool vis[MAXN];
inline bool dfs(int x) {
for(int p:G[x]) {
if(vis[p]) continue;
vis[p]=true;
if(tar[p]==-1||dfs(tar[p])) {
tar[p]=x;
return true;
}
}
return false;
}
signed main() {
int n;
scanf("%d",&n);
for(int i=1;i<=n;++i) scanf("%d",&a[i]);
memset(vis,false,sizeof(vis));
for(int tot=0,i=1;i<=n;++i) {
if(ia[i]) continue;
++tot;
while(!vis[i]) {
vis[i]=true,ia[i]=tot;
i=a[i];
}
}
for(int i=1;i<=n;++i) scanf("%d",&b[i]);
memset(vis,false,sizeof(vis));
for(int tot=0,i=1;i<=n;++i) {
if(ib[i]) continue;
++tot;
while(!vis[i]) {
vis[i]=true,ib[i]=tot;
i=b[i];
}
}
memset(tar,-1,sizeof(tar));
for(int i=1;i<=n;++i) G[ia[i]].push_back(ib[i]);
int tot=n;
for(int i=1;i<=n;++i) {
memset(vis,false,sizeof(vis));
if(dfs(i)) --tot;
}
printf("%d\n",tot);
for(int i=1;i<=n;++i) {
if(tar[ib[i]]==ia[i]) tar[ib[i]]=-1;
else printf("%d ",i);
}
puts("");
return 0;
}

Educational Codeforces Round 141 解题报告的更多相关文章

  1. Codeforces Round #300 解题报告

    呜呜周日的时候手感一直很好 代码一般都是一遍过编译一遍过样例 做CF的时候前三题也都是一遍过Pretest没想着去检查... 期间姐姐提醒说有Announcement也自信不去看 呜呜然后就FST了 ...

  2. Codeforces Round #513解题报告(A~E)By cellur925

    我是比赛地址 A:Phone Numbers $Description$:给你一串数字,问你能组成多少开头为8的11位电话号码. $Sol$:统计8的数量,与$n$%11作比较. #include&l ...

  3. Codeforces Round #302 解题报告

    感觉今天早上虽然没有睡醒但是效率还是挺高的... Pas和C++换着写... 544A. Set of Strings   You are given a string q. A sequence o ...

  4. Codeforces Round #301 解题报告

    感觉这次的题目顺序很不合理啊... A. Combination Lock   Scrooge McDuck keeps his most treasured savings in a home sa ...

  5. 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 :否则 ...

  6. [Educational Codeforces Round 16]E. Generate a String

    [Educational Codeforces Round 16]E. Generate a String 试题描述 zscoder wants to generate an input file f ...

  7. [Educational Codeforces Round 16]D. Two Arithmetic Progressions

    [Educational Codeforces Round 16]D. Two Arithmetic Progressions 试题描述 You are given two arithmetic pr ...

  8. [Educational Codeforces Round 16]C. Magic Odd Square

    [Educational Codeforces Round 16]C. Magic Odd Square 试题描述 Find an n × n matrix with different number ...

  9. [Educational Codeforces Round 16]B. Optimal Point on a Line

    [Educational Codeforces Round 16]B. Optimal Point on a Line 试题描述 You are given n points on a line wi ...

随机推荐

  1. 知识图谱-生物信息学-医学顶刊论文(Bioinformatics-2021)-KG4SL:用于人类癌症综合致死率预测的知识图神经网络

    5.(2021.7.12)Bioinformatics-KG4SL:用于人类癌症综合致死率预测的知识图神经网络 论文标题:KG4SL: knowledge graph neural network f ...

  2. 驱动开发:内核监视LoadImage映像回调

    在笔者上一篇文章<驱动开发:内核注册并监控对象回调>介绍了如何运用ObRegisterCallbacks注册进程与线程回调,并通过该回调实现了拦截指定进行运行的效果,本章LyShark将带 ...

  3. C# 6.0 添加和增强的功能【基础篇】

    C# 6.0 是在 visual studio 2015 中引入的.此版本更多关注了语法的改进,让代码更简洁且更具可读性,使编程更有效率,而不是和前几个版本一样增加主导性的功能. 一.静态导入 我们都 ...

  4. 题解 P6745 『MdOI R3』Number

    前言 不知道是不是正解但是觉得挺好理解. 科学计数法 将一个数表示为\(a\times 10^x\) 的形式.其中\(a\leq10\),\(x\) 为整数. \(\sf Solution\) 其实这 ...

  5. Mybatis 报错Mapper method 'xxx' has an unsupported return type

    报错原因: 出现这种错误,说明sql语句执行成功,只是返回类型出了问题. 解决方法: insert.delete.update操作默认返回一个int类型的整数,将增删改的接口改成int或者void即可 ...

  6. mybatis-核心配置文件讲解

    核心配置文件详解 核心配置文件中的标签必须按照固定的顺序(有的标签可以不写,但顺序一定不能乱): properties.settings.typeAliases.typeHandlers.object ...

  7. 几个实用 shell 脚本

    1. Dos攻击防范(自动屏蔽攻击 IP) #!/bin/bash DATE=$(date +%d/%b/%Y:%H:%M) LOG_FILE=/usr/local/nginx/logs/demo2. ...

  8. python 基本使用 异常判断

    简单常用 isinstance 判断一个对象是否是一个已知的类型 arg=123 isinstance(arg, int) #输出True isinstance(arg, str) #输出False ...

  9. go:快速添加接口方法及其实现

    问题描述 在大型项目中,通常存在多个模块,模块对外暴露的功能通常是通过接口封装,这样可以明确模块的功能,有效降低模块与模块之间的耦合度,同时模块与模块之间进行合理的组装.接口的实现,有时可能存在多个实 ...

  10. 【题解】CF991C Candies

    题面传送门 解决思路 看到 \(10^{18}\) 的范围,我们可以想到二分答案.只要对于每一个二分出的答案进行 \(check\) ,如果可行就往比它小的半边找,不可行就往比它大的半边找. 以下是 ...