Codeforces Round #842 (Div. 2)A~D

文章包含三个编程问题的解决方案,涉及阶乘性质、快速排序以及元素重组。在A题中,利用阶乘的性质找到特定值;B题中,计算一次操作能将多少个数排序;C题中,探讨如何解压给定数组并保持条件。这些问题都涉及到算法和数学知识。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

A. Greatest Convex

x ! + ( x − 1 ) ! = 1 ∗ 2 ∗ . . . ∗ ( x − 1 ) ∗ ( x + 1 ) x!+(x-1)!=1*2*...*(x-1)*(x+1) x!+(x1)!=12...(x1)(x+1)
然后 1 ≤ x < k 1{\leq}x<k 1x<k,所以x=k-1就行了

#include <iostream>
#include <cstring>
#include <algorithm>
#define fir(i,a,b) for(int i=a;i<=b;i++)
typedef long long LL;
#define met(x,y) memset(x,y,sizeof x)
//#define read(x) scanf("%d",&x)
using namespace std;
typedef pair<int,int> PII;
int read()
{
char ch = getchar(); int x = 0, w = 1;
while (ch < '0' || ch>'9') { if (ch == '-')w = -1; ch = getchar(); }
while (ch >= '0' && ch <= '9') { x = (x << 1) + (x << 3) + ch - '0';
ch = getchar(); }
return x*w;
}
int main()
{
//freopen("test.txt", "r", stdin);
int t=read();
while(t--)
{
int n=read();
cout<<n-1<<endl;
}
//freopen("CON", "r", stdin);
//system("pause");
 return 0;
}

B. Quick Sort

一次操作可以将k个数有序,那么就可以计算排列有序的数量,即n在p数组的下标大于n-1在p数组的下标,n从2开始看能到几。

#include <iostream>
#include <cstring>
#include <algorithm>
#define fir(i,a,b) for(int i=a;i<=b;i++)
typedef long long LL;
#define met(x,y) memset(x,y,sizeof x)
//#define read(x) scanf("%d",&x)
using namespace std;
typedef pair<int,int> PII;
const int N = 1e5+10;
int a[N];
int read()
{
char ch = getchar(); int x = 0, w = 1;
while (ch < '0' || ch>'9') { if (ch == '-')w = -1; ch = getchar(); }
while (ch >= '0' && ch <= '9') { x = (x << 1) + (x << 3) + ch - '0';
ch = getchar(); }
return x*w;
}
int main()
{
//freopen("test.txt", "r", stdin);
int t=read();
while(t--)
{
int n=read(),k=read();
for(int i=0;i<n;i++)a[i]=read();
int t=1;
for(int i=0;i<n;i++)if(a[i]==t)t++;
if(t>n)puts("0");
else cout<<(n-t)/k+1<<endl;
}
//freopen("CON", "r", stdin);
//system("pause");
 return 0;
}

C. Elemental Decompress

设最终2个数组为 x [ N ] , y [ N ] x[N],y[N] x[N],y[N]
先用一个数组 c n t [ N ] cnt[N] cnt[N]记下每个数出现的次数,如果有一个数的次数大于2就可以直接NO了
再用两个数组 l [ N ] , r [ N ] l[N],r[N] l[N],r[N]记下,每个数出现的位置,因为最多两次,所以用两个数组
再从1到n遍历一下

  • 如果 i i i出现的次数为1, x [ l [ i ] ] = i , y [ l [ i ] = i x[l[i]]=i,y[l[i]=i x[l[i]]=i,y[l[i]=i
  • 如果 i i i出现的次数为2,可以用一个 v e c t o r < i n t > c y vector<int>cy vector<int>cy记录i
  • 如果 i i i出现的次数为0,可以用一个 v e c t o r < i n t > c x vector<int>cx vector<int>cx记录i

对cx和cy进行sort一下
如果 c x . s i z e ( ) ! = c y . s i z e ( ) cx.size()!=cy.size() cx.size()!=cy.size()或者 c x [ i ] > c y [ i ] cx[i]>cy[i] cx[i]>cy[i]就NO
再遍历一下cy让所有的 c y [ i ] 对应 c x [ i ] cy[i]对应cx[i] cy[i]对应cx[i]

#include <iostream>
#include <cstring>
#include <algorithm>
#include <vector>
#define fir(i, a, b) for (int i = a; i <= b; i++)
typedef long long LL;
#define met(x, y) memset(x, 0, 4*(n+5))
//#define read(x) scanf("%d",&x)
using namespace std;
typedef pair<int, int> PII;
const int N = 2e5 + 10;
int a[N];
int x[N], y[N];
int cnt[N];
int l[N], r[N];
vector<int> cx, cy;
int read()
{
    char ch = getchar();
    int x = 0, w = 1;
    while (ch < '0' || ch > '9')
    {
        if (ch == '-')
            w = -1;
        ch = getchar();
    }
    while (ch >= '0' && ch <= '9')
    {
        x = (x << 1) + (x << 3) + ch - '0';
        ch = getchar();
    }
    return x * w;
}
int main()
{
    // freopen("test.txt", "r", stdin);
    int t = read();
    while (t--)
    {
        int n = read();
        met(cnt, n);
        met(l, n);
        met(r, n);
        cx.clear(), cy.clear();
        
        bool fl = true;
        for (int i = 1; i <= n; i++)
            a[i] = read();
        for (int i = 1; i <= n; i++)
        {
            cnt[a[i]]++;
            if (l[a[i]] == 0)
                l[a[i]] = i;
            else
                r[a[i]] = i;
            if (cnt[a[i]] > 2)
                fl = false;
        }
        if (!fl)
        {
            puts("NO");
            continue;
        }
        for (int i = 1; i <= n; i++)
        {
            if (cnt[i] == 1)
                x[l[i]] = i, y[l[i]] = i;
            else if (cnt[i] == 0)
                cx.push_back(i);
            else
                cy.push_back(i);
        }
        //cout<<cx.size();
        if (cx.size() != cy.size())
            fl = false;
        if (!fl)
        {
            puts("NO");
            continue;
        }
        for (int i = 0; i < cx.size(); i++)
            if (cx[i] > cy[i])
                fl = false;
        if (!fl)
        {
            
            puts("NO");
            continue;
        }
        for(int i=0;i<cy.size();i++)
        {
            x[l[cy[i]]]=cy[i],y[r[cy[i]]]=cy[i],y[l[cy[i]]]=cx[i],
            x[r[cy[i]]]=cx[i];
        }
        puts("YES");
        for(int i=1;i<=n;i++)cout<<x[i]<<" ";
        puts("");
        for(int i=1;i<=n;i++)cout<<y[i]<<" ";
        puts("");
    }
    // freopen("CON", "r", stdin);
    // system("pause");
    return 0;
}

D. Lucky Permutation

这个当时一点思路都没有,主要是看这个
链接
我复制粘贴一下,代码自己写的

本题可能需要了解群论里的部分结论.
首先逆序对为1其实就是在1,2,3,⋯,n单位排列的基础上交换任意一对相邻的数字.
此时排列一定有n−2个自环和1个二元置换组成,并且这个二元置换里两个元素只差1.
一次交换操作可以造成两种效果

  1. 把一个大环拆成两个小环.
  2. 把两个小环合并成一个大环.

所以我们可以处理出初始排列中的所有置换环,设共有m个环,每个环的长度为 c i c_i ci,令 s = ∑ i = 1 m ( c i − 1 ) s=\sum_{i=1}^{m}(c_i-1) s=i=1m(ci1),则s就是把原排列变成1,2,3,⋯,n单位排列需要的交换次数.

  1. 如果初始排列是1,2,3,⋯,n单位排列,显然需要一次操作.
  2. 如果某个长度大于等于2的环中存在两个相邻的元素,则我们在拆这个存在相邻元素的环时可以最后留下一个相邻元素组成的二元环,所以答案为 s = ∑ i = 1 m ( c i − 1 ) − 1 s=\sum_{i=1}^{m}(c_i-1)-1 s=i=1m(ci1)1.
  3. 否则所有环中均不存在相邻元素,我们需要先把初始排列还原成单位排列然后任意交换一对相邻元素,答案为 s = ∑ i = 1 m ( c i − 1 ) + 1 s=\sum_{i=1}^{m}(c_i-1)+1 s=i=1m(ci1)+1
#include <iostream>
#include <cstring>
#include <algorithm>
#include <vector>
#define fir(i,a,b) for(int i=a;i<=b;i++)
typedef long long LL;
#define met(x) memset(x,0,sizeof x)
//#define read(x) scanf("%d",&x)
using namespace std;
typedef pair<int,int> PII;
const int N = 2e5+10;
int a[N];
bool st[N];
vector<int> ct[N];
int cnt=0;
int read()
{
char ch = getchar(); int x = 0, w = 1;
while (ch < '0' || ch>'9') { if (ch == '-')w = -1; ch = getchar(); }
while (ch >= '0' && ch <= '9') { x = (x << 1) + (x << 3) + ch - '0';
ch = getchar(); }
return x*w;
}
int main()
{
//freopen("test.txt", "r", stdin);
int t=read();
while(t--)
{
    int n=read();
    met(st);
    cnt=0;
    for(int i=0;i<n;i++)ct[i].clear();
    for(int i=1;i<=n;i++)a[i]=read();
    for(int i=1;i<=n;i++)
    {
        if(st[i])continue;
        for(int j=i;!st[j];j=a[j])
        {
            ct[cnt].push_back(j);
            st[j]=true;
        }
        cnt++;
    }
    int fl=1,sum=0;
    for(int i=0;i<cnt;i++)sort(ct[i].begin(),ct[i].end());
    for(int i=0;i<cnt;i++)
    {
        sum+=ct[i].size()-1;
        if(ct[i].size()>=2)for(int j=0;j+1<ct[i].size();j++)
        {
            if(ct[i][j+1]-1==ct[i][j])fl=-1;
        }
    }
    //cout<<sum;
    sum+=fl;
    cout<<sum<<endl;
}
//freopen("CON", "r", stdin);
//system("pause");
 return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值