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,l−1]和
a
[
r
+
1
,
n
]
a[r+1, n]
a[r+1,n]的顺序
思路:
容易得出,最优解情况必然是
n
n
n或
n
−
1
n-1
n−1在答案序列的左端(若初始时
n
n
n已经最左边,那么答案中
n
−
1
n-1
n−1在最左端)
所以翻转区间的右端点必然是最大数或第二大数的位置左边,接下来枚举左端点即可
(注意:若最大数或第二大数在最右端,还需考虑翻转区间为
[
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;
}