【Codeforces Round 874 (Div.3) 萌新题解】

本文详细解答了CodeforcesRound874(Div.3)比赛中的几道编程题目,包括字符串处理、数组操作、图的构建等算法问题,提供了清晰的思路和代码实现。

Codeforces Round 874 (Div.3) 萌新题解

A
题意:
给定一个字符串sss,我们最少可以用多少个长度为2的字符串拼接成它。
拼接规则:只有一个字符串的末尾和另一个字符串的开头相等才能拼接,
且合并的字母只算一次,如abababbababa合并后为abaabaabaacacacbcbcbc不能合并。

思路:
寻找sss中长度为2的不同子串的数量

代码:

#include <bits/stdc++.h>

using namespace std;
#define int long long
#define ull unsigned long long
#define rep(i, a, b) for(int i=a;i<=b;i++)
#define per(i, a, b) for(int i=a,i>=b,i--)

typedef pair<int, int> PII;
const int mod = 1e9 + 7;
const int inf = 0x3f3f3f3f;
map<string,int> mp;
void solve() {
    int n;
    mp.clear();
    cin>>n;
    string s;
    cin>>s;
    int cnt=0;
    for(int i=0;i<s.size()-1;i++)
    {
        mp[s.substr(i,2)]=1;
    }
    
    cout<<mp.size()<<endl;
}

signed main() {
    std::ios::sync_with_stdio(false);
    std::cin.tie(0), cout.tie(0);
    int t;
    cin >> t;
    while (t--)solve();
    return 0;
}

B
题意:
给定两个数n,kn,kn,k
再给定两个长度为nnn的数组a,ba,ba,b,重新排列bbb,使得∣a[i]−b[i]∣<=k|a[i]-b[i]|<=ka[i]b[i]<=k,题目保证有解

思路:
题目保证有解,可以直接将a,b均从大到小排序,一一对应其位置即可

代码:

#include <bits/stdc++.h>
 
using namespace std;
#define int long long
#define ull unsigned long long
#define rep(i, a, b) for(int i=a;i<=b;i++)
#define per(i, a, b) for(int i=a,i>=b,i--)
 
typedef pair<int, int> PII;
const int mod = 1e9 + 7;
const int inf = 0x3f3f3f3f;
int b[200200];
struct node{
    int val;
    int id;
}a[200200];
int ans[200200];
bool cmp(node a,node b)
{
    return a.val<b.val;
}
void solve() {
    int n,k;
    cin>>n>>k;
    for(int i=1;i<=n;i++)cin>>a[i].val,a[i].id=i;
    for(int i=1;i<=n;i++)cin>>b[i];
    sort(a+1,a+n+1,cmp);
    sort(b+1,b+n+1);
    for(int i=1;i<=n;i++)ans[a[i].id]=b[i];
    for(int i=1;i<=n;i++)cout<<ans[i]<<" ";
    cout<<endl;
}
 
signed main() {
    std::ios::sync_with_stdio(false);
    std::cin.tie(0), cout.tie(0);
    int t;
    cin >> t;
    while (t--)solve();
    return;
}

C
题意:
给定一个长度为nnn的数组aaa,构造出满足下列要求的数组bbb
要求:①对任意iii属于[1,n],b[i]>0[1,n],b[i]>0[1,n],b[i]>0,
bbb中每个数都具有相同的奇偶性
b[i]=a[i]b[i]=a[i]b[i]=a[i]b[i]=a[i]−a[j]b[i]=a[i]-a[j]b[i]=a[i]a[j](jjj属于[1,n][1,n][1,n])
有解输出YES
无解输出NO

思路:
首先,若有解,则每个数的奇偶性必然相同,
根据要求①③可知,答案序列的奇偶性依赖于原序列最小数的奇偶性
若最小数为奇数,则答案序列总有方案得到奇数,必然有解
最小数为偶数时,那么在原序列中若出现了奇数,则无解,偶数时有解

代码:

#include <bits/stdc++.h>

using namespace std;
#define int long long
#define ull unsigned long long
#define rep(i, a, b) for(int i=a;i<=b;i++)
#define per(i, a, b) for(int i=a,i>=b,i--)

typedef pair<int, int> PII;
const int mod = 1e9 + 7;
const int inf = 0x3f3f3f3f;
int a[200200];
int b[200200];
void solve() {
    int n;
    cin>>n;
    int min1=inf;
    for(int i=1;i<=n;i++)cin>>a[i],min1=min(min1,a[i]);
    if(min1%2){
        cout<<"YES"<<endl;
    }
    else{
        for(int i=1;i<=n;i++)
        {
            if(a[i]%2){
                cout<<"NO"<<endl;
                return;
            }
        }
        cout<<"YES"<<endl;
    }
}

signed main() {
    std::ios::sync_with_stdio(false);
    std::cin.tie(0), cout.tie(0);
    int t;
    cin >> t;
    while (t--)solve();
    return 0;
}

D
题意:
给定一个长为nnn的排列,请你在必须翻转且只能翻转一次的基础上找到字典序最大的排列,
翻转规则:若翻转区间为[l,r][l, r][l,r],那么先翻转区间[l,r][l,r][l,r]的每个数,再调换a[1,l−1]a[1, l-1]a[1,l1]a[r+1,n]a[r+1, n]a[r+1,n]的顺序

思路:
容易得出,最优解情况必然是nnnn−1n-1n1在答案序列的左端(若初始时nnn已经最左边,那么答案中n−1n-1n1在最左端)
所以翻转区间的右端点必然是最大数或第二大数的位置左边,接下来枚举左端点即可
(注意:若最大数或第二大数在最右端,还需考虑翻转区间为[n,n][n,n][n,n]的情况)
复杂度O(n2)O(n^2)O(n2)

代码:

#include <bits/stdc++.h>

using namespace std;
#define int long long
#define ull unsigned long long
#define rep(i, a, b) for(int i=a;i<=b;i++)
#define per(i, a, b) for(int i=a,i>=b,i--)

typedef pair<int, int> PII;
const int mod = 1e9 + 7;
const int inf = 0x3f3f3f3f;
int a[200200];
int tmp[200200];
int ans[200200];
void solve() {
    int n;
    cin>>n;
    int pos1,pos2;
    for(int i=1;i<=2000;i++)
    {
        ans[i]=0;
        tmp[i]=0;
    }
    for(int i=1;i<=n;i++){
        cin>>a[i];
        if(a[i]==n){
            pos1=i;
        }
        if(a[i]==n-1){
            pos2=i;
        }
    }
    if(n==1)
    {
        cout<<1<<endl;
        return;
    }
    if(pos1==1)
    {
        pos1=pos2;
    }
        for(int i=1;i<=pos1-1;i++)
        {
            int l=i,r=pos1-1;
            int cnt=0;
            for(int j=pos1;j<=n;j++)
            {
                tmp[++cnt]=a[j];
            }
            for(int j=pos1-1;j>=l;j--)
            {
                tmp[++cnt]=a[j];
            }
            for(int j=1;j<l;j++)
            {
                tmp[++cnt]=a[j];
            }
            for(int j=1;j<=n;j++)
            {
                if(tmp[j]>ans[j])
                {
                    for(int k=1;k<=n;k++)
                    {
                        ans[k]=tmp[k];
                    }
                    break;
                }
                if(tmp[j]==ans[j])continue;
                if(tmp[j]<ans[j]){
                    break;
                }
            }
        }
   if(pos1==n)
   {
       tmp[1]=a[n];
       for(int i=1;i<=n-1;i++)
       {
           tmp[i+1]=a[i];
       }
       for(int j=1;j<=n;j++)
       {
           if(tmp[j]>ans[j])
           {
               for(int k=1;k<=n;k++)
               {
                   ans[k]=tmp[k];
               }
               break;
           }
           if(tmp[j]==ans[j])continue;
           if(tmp[j]<ans[j]){
               break;
           }
       }
   }
   for(int i=1;i<=n;i++)cout<<ans[i]<<" ";
   cout<<endl;
}

signed main() {
    std::ios::sync_with_stdio(false);
    std::cin.tie(0), cout.tie(0);
    int t;
    cin >> t;
    while (t--)solve();
    return 0;
}

E
题意:
给定长为nnn的序列aaa,这个序列描述的是,编号为iii的节点与编号为a[i]a[i]a[i]的节点之间有一条无向边,
现在可以继续添加边(也可以不加),须保持每个节点所能连接到的点数不超过两个
问能构造的联通块最大值和最小值?
思路:
最大值即为不加任何的边时 当前连通块的数量,最小值是把所有链连接起来
先用并查集得到rootrootroot数,即为maxmaxmax
接下来找度数为1的点,即为链的端点
链数不为0时min=maxmin=maxmin=max-链数+1;
链数为0时 min=maxmin=maxmin=max

代码:

#include <bits/stdc++.h>
 
using namespace std;
#define int long long
#define ull unsigned long long
#define rep(i, a, b) for(int i=a;i<=b;i++)
#define per(i, a, b) for(int i=a,i>=b,i--)
 
typedef pair<int, int> PII;
const int mod = 1e9 + 7;
const int inf = 0x3f3f3f3f;
int a[200200];
int fa[200100];
map <int,int> mp;
void init(int n)
{
    for(int i=1;i<=n;i++)fa[i]=i;
}
int find(int x)
{
    if(fa[x]==x)
        return x;
    fa[x]=find(fa[x]);
    return find(fa[x]);
}
void merge(int x,int y)
{
    fa[find(x)]=find(y);
}
 
void solve() {
    mp.clear();
    int n;
    cin>>n;
    init(n);
    vector <vector<int> > edge(n+1);
    int max1=0,min1=inf;
    for(int i=1;i<=n;i++){
    cin>>a[i];
    edge[i].push_back(a[i]);
    edge[a[i]].push_back(i);
    merge(i,a[i]);
    }
    for(int i=1;i<=n;i++)
    {
        if(!mp[find(i)])
        {
            max1++;
        }
            mp[find(i)]++;
    }
    //cout<<max1<<endl;
    int cnt=0;
    for(int i=1;i<=n;i++)
    {
        int deg=unique(edge[i].begin(),edge[i].end())-edge[i].begin();
        if(deg==1)cnt++;
    }
    min1=max1-cnt/2;
    if(cnt/2)min1++;
    cout<<min1<<" "<<max1<<endl;
}
 
signed main() {
    std::ios::sync_with_stdio(false);
    std::cin.tie(0), cout.tie(0);
    int t;
    cin >> t;
    while (t--)solve();
    return 0;
}

F
题意:
给定一个长度为nnn的数组,再给定mmm,请你找出数组中的所有满足下要求的子序列数量。
要求:①子序列长度为mmm
②子序列中最大值与最小值之差不超过mmm
③子序列中数字;两两不同。
思路:
记录重复出现过的数字的出现次数
然后排序并去重
从起始位置开始算每个连续段的结果(滑动窗口的思路)
注意取模!而且因为过程中用到除法,需要用到费马小定理求逆元

代码:

#include <bits/stdc++.h>

using namespace std;
#define int long long
#define ull unsigned long long
#define rep(i, a, b) for(int i=a;i<=b;i++)
#define per(i, a, b) for(int i=a,i>=b,i--)

typedef pair<int, int> PII;
const int mod = 1e9 + 7;
const int inf = 0x3f3f3f3f;
int a[200200];
int qpow(int x,int n)
{
    int ans=1;
    while(n)
    {
        if(n&1)
        {
            ans	= (ans*x)%mod;
        }
        x=(x*x)%mod;
        n>>=1;

    }
    return ans;
}

void solve() {
    int n,m;
    cin>>n>>m;
    map <int,int> cnt;
    for(int i=1;i<=n;i++)cin>>a[i];
    for(int i=1;i<=n;i++)
    {
        cnt[a[i]]++;
    }
    sort(a+1,a+n+1);
    n=unique(a+1,a+n+1)-(a+1);
    int ans=0;
    int res=1;
    int r=1;
    for(int i=1;i+m-1<=n;i++)
    {
        while(a[r]-a[i]<m&&r<=n)res=res*(cnt[a[r++]])%mod;
        if(r-i==m)ans=(ans+res)%mod;
        res=res*qpow(cnt[a[i]],mod-2)%mod;
    }
    cout<<ans<<endl;
}

signed main() {
    std::ios::sync_with_stdio(false);
    std::cin.tie(0), cout.tie(0);
    int t;
    cin >> t;
    while (t--)solve();
    return 0;
}

G
题意:
给定一棵nnn个节点的树,请你减掉一些边,使得剪掉后的每个树只有三个节点,
如果可以,第一行返回减掉边的数量,第二行返回减掉边的编号;如果无解,输出-1。

思路:
nnn modmodmod 3≠03≠03=0无解
考虑采用dfsdfsdfs寻找每个点的子树的大小,如果正好为3时,切掉它和它的父亲节点那条边(用了map<int,map<int,int>>map<int,map<int,int> >map<int,map<int,int>> 存边)
当某个点的子树大小超过了3,必然无解

代码:

#include <bits/stdc++.h>

using namespace std;
#define int long long
#define ull unsigned long long
#define rep(i, a, b) for(int i=a;i<=b;i++)
#define per(i, a, b) for(int i=a,i>=b,i--)

typedef pair<int, int> PII;
const int mod = 1e9 + 7;
const int inf = 0x3f3f3f3f;
vector <vector<int> > mp(200200);
vector <int> ans;
map <int,map<int,int> > edge;
int dfs(int u,int fa)
{
    int cnt=1;
    for(auto v:mp[u])
    {
        if(v==fa)continue;
        cnt+=dfs(v,u);
    }
    if(cnt==3){
        ans.push_back(edge[u][fa]);
        return 0;
    }
    return cnt;
}
void solve() {
    int n;
    cin>>n;
    for(int i=0;i<=n;i++)mp[i].clear(),edge[i].clear();
    ans.clear();
    for(int i=1;i<=n-1;i++)
    {
        int u,v;
        cin>>u>>v;
        mp[u].push_back(v);
        mp[v].push_back(u);
        edge[u][v]=i;
        edge[v][u]=i;
    }
    if(n%3){
        cout<<-1<<endl;
        return;
    }
    int q=dfs(1,0);
    if(ans.size()!=n/3)
    {
        cout<<-1<<endl;
    }
    else{
        cout<<n/3-1<<endl;
        for(int i=0;i<ans.size()-1;i++)
            cout<<ans[i]<<" ";
        cout<<endl;
    }
}

signed main() {
    std::ios::sync_with_stdio(false);
    std::cin.tie(0), cout.tie(0);
    int t;
    cin >> t;
    while (t--)solve();
    return 0;
}
### 完美排列问题分析 对于 Codeforces Round 1007 (Div. 2) 中的 **B. Perfecto** 问题,目标是找到一个长度为 \( n \) 的完美排列。如果这样的排列存在,则输出该排列;否则输出 `-1`。 #### 题目解析 题目定义了一个“完美排列”,其条件如下: - 对于任意位置 \( i \),满足 \( |p_i - p_{i+1}| = 1 \) 或者 \( |p_i - p_{i+1}| = n-1 \)[^2]。 这意味着相邻两个元素之间的差值要么等于 1(即连续),要么等于 \( n-1 \)(即首尾相连)。 #### 解题方法 通过观察和归纳可以得出以下结论: - 当 \( n \% 3 == 0 \) 或 \( n \% 3 == 2 \) 时,无法构建出符合上述条件的完美排列。 - 而当 \( n \% 3 == 1 \) 时,可以通过特定构造方式生成所需排列。 具体实现逻辑如下: ```cpp #include <bits/stdc++.h> using namespace std; void solve() { int n; cin >> n; if (n % 3 != 1) { // 如果不符合模数条件 cout << "-1\n"; return; } vector<int> res(n); bool flag = true; // 控制交替模式 for(int i = 0;i < n;i++) { if(flag){ res[i] = i + 1; } else{ res[i] = ((n-i)+1)%n; if(res[i]==0)res[i]=n; } flag=!flag; } for(auto num : res){ cout<<num<<" "; } } int main(){ ios::sync_with_stdio(false); cin.tie(0); int t=1; while(t--){ solve(); } } ``` 此代码片段实现了基于输入大小 \( n \) 来判断是否存在合法解并输出相应结果的功能。 #### 关键点说明 - 判断依据来源于对不同余数值下能否形成循环结构的研究成果。 - 构造过程中采用交替填充策略来确保最终序列能够满足绝对差的要求。 --- ###
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值