Codeforces Round 874 (Div.3) 萌新题解
A
题意:
给定一个字符串sss,我们最少可以用多少个长度为2的字符串拼接成它。
拼接规则:只有一个字符串的末尾和另一个字符串的开头相等才能拼接,
且合并的字母只算一次,如ababab和bababa合并后为abaabaaba,acacac和bcbcbc不能合并。
思路:
寻找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]|<=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
题意:
给定一个长度为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,l−1]和a[r+1,n]a[r+1, n]a[r+1,n]的顺序
思路:
容易得出,最优解情况必然是nnn或n−1n-1n−1在答案序列的左端(若初始时nnn已经最左边,那么答案中n−1n-1n−1在最左端)
所以翻转区间的右端点必然是最大数或第二大数的位置左边,接下来枚举左端点即可
(注意:若最大数或第二大数在最右端,还需考虑翻转区间为[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;
}
本文详细解答了CodeforcesRound874(Div.3)比赛中的几道编程题目,包括字符串处理、数组操作、图的构建等算法问题,提供了清晰的思路和代码实现。
1150

被折叠的 条评论
为什么被折叠?



