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

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

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

思路:
寻找 s s s中长度为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 , k n,k n,k
再给定两个长度为 n n n的数组 a , b a,b a,b,重新排列 b b b,使得 ∣ a [ i ] − b [ i ] ∣ < = k |a[i]-b[i]|<=k a[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
题意:
给定一个长度为 n n n的数组 a a a,构造出满足下列要求的数组 b b b
要求:①对任意 i i i属于 [ 1 , n ] , b [ i ] > 0 [1,n],b[i]>0 [1,n],b[i]>0,
b b b中每个数都具有相同的奇偶性
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]( j j j属于 [ 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
题意:
给定一个长为 n n n的排列,请你在必须翻转且只能翻转一次的基础上找到字典序最大的排列,
翻转规则:若翻转区间为 [ 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]的顺序

思路:
容易得出,最优解情况必然是 n n n n − 1 n-1 n1在答案序列的左端(若初始时 n n n已经最左边,那么答案中 n − 1 n-1 n1在最左端)
所以翻转区间的右端点必然是最大数或第二大数的位置左边,接下来枚举左端点即可
(注意:若最大数或第二大数在最右端,还需考虑翻转区间为 [ n , n ] [n,n] [n,n]的情况)
复杂度 O ( n 2 ) 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
题意:
给定长为 n n n的序列 a a a,这个序列描述的是,编号为 i i i的节点与编号为 a [ i ] a[i] a[i]的节点之间有一条无向边,
现在可以继续添加边(也可以不加),须保持每个节点所能连接到的点数不超过两个
问能构造的联通块最大值和最小值?
思路:
最大值即为不加任何的边时 当前连通块的数量,最小值是把所有链连接起来
先用并查集得到 r o o t root root数,即为 m a x max max
接下来找度数为1的点,即为链的端点
链数不为0时 m i n = m a x min=max min=max-链数+1;
链数为0时 m i n = m a x min=max min=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
题意:
给定一个长度为 n n n的数组,再给定 m m m,请你找出数组中的所有满足下要求的子序列数量。
要求:①子序列长度为 m m m
②子序列中最大值与最小值之差不超过 m m m
③子序列中数字;两两不同。
思路:
记录重复出现过的数字的出现次数
然后排序并去重
从起始位置开始算每个连续段的结果(滑动窗口的思路)
注意取模!而且因为过程中用到除法,需要用到费马小定理求逆元

代码:

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

思路:
n n n m o d mod mod 3 ≠ 0 3≠0 3=0无解
考虑采用 d f s dfs dfs寻找每个点的子树的大小,如果正好为3时,切掉它和它的父亲节点那条边(用了 m a p < i n t , m a p < i n t , i n t > > 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;
}
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值