news 2026/5/26 2:31:51

牛客周赛 Round 111

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
牛客周赛 Round 111

设一个数组

=

{

2

,

3

,

4

,

3

,

5

,

1

}

b={2,3,4,3,5,1},则

(

)

=

2

+

3

+

4

+

5

=

14

L(b)=2+3+4+5=14,

(

)

=

1

+

5

=

6

R(b)=1+5=6。

小芳希望小红构造一个长为

n 的排列

c,使得

(

)

+

(

)

=

L(c)+R(c)=k。请你帮帮小红。

也就是说大概是这样的:

联想截图_20250929164249

找到最高的位置,记为

p,那么

(

)

L(a)也就等于

[

1

,

]

[1,p]单调增的所有数相加;

(

)

R(a)等于

[

,

]

[p,n]从右向左单调增的所有数之和。由于答案要求我们构造一个长度为

n的排列,因此

n肯定会被计算两次,

1

n−1会被计算一次,所以

(

)

+

(

)

3

×

1

L(a)+R(a)≥3×n−1。

考虑如何构造:不妨把

n方在最后一个位置,

1

n−1放在

n前面的位置,然后将构造

k需要的数按大到小放在

1

n−1的前面,没有用到的数放在

1

n−1和

n的中间即可。对于从

[

1

,

2

]

[1,n−2]构造

(

3

×

1

)

k−(3×n−1),只需要贪心即可。

void solve() {

int n,k;

cin >> n >> k;

if(k<3*n-1||k>((1+n)*n/2+n)){

cout << -1 << endl;

return;

}

k-=3*n-1;

vector<int> vis(n+1);

vis[n]=vis[n-1]=1;

for(int i=n-2;i>=1;i--){

if(k>=i){

vis[i]=1;

k-=i;

}

}

if(k!=0){

cout << -1 << endl;

return;

}

vector<int> ans;

for(int i=1;i<=n-1;i++){

if(vis[i]) ans.push_back(i);

}

for(int i=1;i<=n-1;i++){

if(!vis[i]) ans.push_back(i);

}

ans.push_back(n);

for(int i:ans) cout << i << " ";

cout << endl;

}

F

对于一个数组,小苯可以做任意次如下操作:

∙ 交换数组中的任意两个元素。

现在小红想要构造一个长为

n 的排列

a,对所有的

a

i

都满足

a

i

=i,使得小苯将数组变为升序的最小操作数为

k。

请你帮帮小红。

题意:现在要求我们构造一个排列,要求对于任意

a

i

=i,且将其变为

=

a

i

=i的最小操作次数为

k,一次操作可以交换排列中任意两个元素的位置。

前置知识:置换环。

我们考虑这样一个排列:

2 3 4 5 1

将其变为升序的最小操作次数为4次,也就是环的大小减1。我们可以对上面这样一个排列建图:

(

,

)

(i,a

i

)。我们通过

dfs可以得到

m个连通块,且每个连通块都是一个环,考虑要将这

m个连通块进行交换得到

n个自环的最小操作次数:首先可以知道交换一定是在环内,连通块之间交换一定是不优的;其次连通块内的最小操作次数为

1

sz

i

−1,其中

sz

i

为第

i个连通块的大小。这个也是很容易证明的。所以我们可以得到结论:最小操作次数

=

=

1

(

1

)

=

=

1

=

=∑

i=1

m

(sz

i

−1)=∑

i=1

m

sz

i

−m=n−m。

现在要求我们最小操作次数为

k,所以连通块的数量一定为

n−k,所以我们只需要构造有

n−k个连通块的排列就好。可以按照这样的方式构造一个连通块:对于

[

1

,

]

[1,m],我们要将其构造为一个置换环,只需要让所有元素向左或者右轮循一次就即可,变为

2

,

3

,

4

,

5

,

6

,

,

,

1

2,3,4,5,6,…,m,1。

那么答案可以贪心构造,每次选择两个元素进行构造,最后所有元素为一组,可以知道环最多为

2

2

n

个,所以可以先对

k进行边界特判,最小操作次数肯定为

2

n−

2

n

,最大操作次数为

1

n−1。

void solve() {

int n,k;

cin >> n >> k;

if(k<(n-n/2)||k>n-1){

cout << -1 << endl;

return;

}

int l=1,r=2;

int m=n-k;

vector<int> ans;

while(m>1){

ans.push_back(r);

ans.push_back(l);

l=r+1;

r=l+1;

m--;

}

//最后一个环

r=n;

for(int i=l+1;i<=n;i++) ans.push_back(i);

ans.push_back(l);

for(int i=1;i<=n;i++){

cout << ans[i-1] << " \n"[i==n];

}

}

再多补一个置换环的题:https://codeforces.com/gym/630599/problem/G

题意为给定一个排列,现在可以交换任意两个元素,问使得排列有恰好一个逆序对的最小操作次数。

很显然答案肯定为这个样子

2

,

1

,

3

,

4

,

5

,

6

,

,

2,1,3,4,5,6,…,n,所以也就是我们将排列变为这个的最小操作次数。由上面的结论,我们可以知道将排列变为

1

,

2

,

3

,

,

1,2,3,…,n的最小操作次数为

n−m,

m为置换环的个数,之后再使用用一次将其变为答案这样。

现在只需要考虑会不会存在比这个操作次数更小的情况,我们发现如果恰好

1

2

1和2在同一个置换环内,那么答案为

1

n−m−1,否则为

+

1

n−m+1。

#include <bits/stdc++.h>

using namespace std;

#define inf 1e18

#define endl '\n'

#define int long long

typedef long long ll;

typedef pair<int, int> pii;

int dx[4] = {1, 0, -1, 0}, dy[4] = {0, 1, 0, -1};

const int N = 2e5 + 9, M = 2e5 + 9, mod = 1e9 + 7;

void solve() {

int n;

cin >> n;

vector<int> p(n+1);

for(int i=1;i<=n;i++) cin >> p[i];

vector<vector<int>> g(n+1);

for(int i=1;i<=n;i++){

g[i].push_back(p[i]);

}

//可以使用tarjan进行缩点

vector<int> dfn(n+1),low(n+1),color(n+1),inq(n+1);

stack<int> stk;

int sscnt=0,tot=0;

auto tarjan=[&](auto&& self,int u)->void{

dfn[u]=low[u]=++tot;

stk.push(u);

inq[u]=1;

for(int v:g[u]){

if(!dfn[v]){

self(self,v);

low[u]=min(low[u],low[v]);

}else if(inq[v]){

low[u]=min(low[u],dfn[v]);

}

}

if(low[u]==dfn[u]){

int v;

sscnt++;

do{

v=stk.top();

color[v]=sscnt;

stk.pop();

inq[v]=0;

}while(v!=u);

}

};

for(int i=1;i<=n;i++){

if(!dfn[i])

tarjan(tarjan,i);

}

int ans=n-sscnt;

if(color[1]==color[2]) ans--;

else ans++;

cout << ans << endl;

}

/*

5 1 4 3 2

5 1 2->2 1 5

4 3

*/

signed main() {

ios::sync_with_stdio(0);

cin.tie(0), cout.tie(0);

int t = 1;

cin >> t;

while (t--) {

solve();

}

return 0;

}

但是wa了,为什么呢?考虑有问题,因为答案的形式应该为

1

,

2

,

3

,

4

,

5

,

,

1,2,3,4,5,…,n,只交换其中一对相邻元素的形式,所以我们还需要对每个

(

,

+

1

)

(i,i+1)考虑是否在同一个置换环内,如果一个都没有,那么答案为

+

1

n−m+1,否则为

1

n−m−1。

#include <bits/stdc++.h>

using namespace std;

#define inf 1e18

#define endl '\n'

#define int long long

typedef long long ll;

typedef pair<int, int> pii;

int dx[4] = {1, 0, -1, 0}, dy[4] = {0, 1, 0, -1};

const int N = 2e5 + 9, M = 2e5 + 9, mod = 1e9 + 7;

void solve() {

int n;

cin >> n;

vector<int> p(n+1);

for(int i=1;i<=n;i++) cin >> p[i];

vector<vector<int>> g(n+1);

for(int i=1;i<=n;i++){

g[i].push_back(p[i]);

}

//可以使用tarjan进行缩点

vector<int> dfn(n+1),low(n+1),color(n+1),inq(n+1);

stack<int> stk;

int sscnt=0,tot=0;

auto tarjan=[&](auto&& self,int u)->void{

dfn[u]=low[u]=++tot;

stk.push(u);

inq[u]=1;

for(int v:g[u]){

if(!dfn[v]){

self(self,v);

low[u]=min(low[u],low[v]);

}else if(inq[v]){

low[u]=min(low[u],dfn[v]);

}

}

if(low[u]==dfn[u]){

int v;

sscnt++;

do{

v=stk.top();

color[v]=sscnt;

stk.pop();

inq[v]=0;

}while(v!=u);

}

};

for(int i=1;i<=n;i++){

if(!dfn[i])

tarjan(tarjan,i);

}

int ans=n-sscnt;

bool ok=false;

for(int i=1;i<n;i++){

if(color[i]==color[i+1]){

ok=true;

break;

}

}

if(ok) ans--;

else ans++;

cout << ans << endl;

}

/*

5 1 4 3 2

5 1 2->2 1 5

4 3

*/

signed main() {

ios::sync_with_stdio(0);

cin.tie(0), cout.tie(0);

int t = 1;

cin >> t;

while (t--) {

solve();

}

return 0;

}

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/5/25 0:58:16

定性与定量考核的结合

在现代企业管理中&#xff0c;如何科学、公正地评估员工绩效&#xff0c;始终是一个核心议题。要实现全面而准确的评估&#xff0c;关键在于将定量考核的客观性与定性考核的深刻性有效结合。 单纯的定量考核&#xff08;“计件”&#xff09;提供了“做什么”的客观数据&#x…

作者头像 李华
网站建设 2026/5/24 11:10:41

如何衡量团队产出效率

在现代组织中&#xff0c;团队的产出效率直接决定企业的竞争力与执行力。**要科学衡量团队产出效率&#xff0c;核心在于建立多维度的指标体系&#xff0c;将成果、过程与协作因素综合评估&#xff0c;以实现对绩效的量化与优化。**单纯用“工作量”或“加班时间”衡量团队贡献…

作者头像 李华
网站建设 2026/5/26 7:23:15

使用格子玻尔兹曼方法(LBM)模拟热扩散的Matlab代码

使用格子玻尔兹曼方法&#xff08;LBM&#xff09;模拟热扩散&#xff0c;Matlab代码格子玻尔兹曼方法&#xff08;LBM&#xff09;搞热扩散模拟其实挺有意思的&#xff0c;今天咱们用Matlab整一个简单的二维版本。先上核心思路&#xff1a;把温度场当作被动标量&#xff0c;用…

作者头像 李华
网站建设 2026/5/26 8:16:08

ORACLE学习笔记总结(数据库参数文件)

Oracle数据库参数文件详解与操作指令 一、参数文件类型概述 Oracle数据库使用两种参数文件来存储实例配置&#xff1a; 1. PFILE&#xff08;Parameter File&#xff09; 文件类型&#xff1a;文本文件&#xff0c;可直接编辑 默认名称&#xff1a;init<SID>.ora&…

作者头像 李华
网站建设 2026/5/26 8:14:12

浅谈:算法中的斐波那契数(六)

方法五&#xff1a;矩阵求幂斐波那契数列矩阵方程&#xff1a;算法&#xff1a;若 N 小于等于 1&#xff0c;则返回 N。使用递归函数matrixPower 计算给定矩阵 A 的幂。幂为 N-1&#xff0c;其中 N 是第 N 个 斐波那契数。matrixPower 函数将对 N/2 个斐波那契数进行操作。在 m…

作者头像 李华