【解题报告】CF DIV2 #ROUND 723 A~D

​比赛链接​

比赛评价:

发现这场十点就开了,然后就和ph巨佬一起玩了一场。我两分别再A和B罚时罚飞了,索性后面把C1,C2整出来了

排名2500+,而且极其刺激,最后3分钟找出C1错误,最后30S交了C1,C2。爽到飞起

【解题报告】CF DIV2 #ROUND 723 A~D_#define

A. Mean Inequality

题意
2*n个数成环,找到一个排列使得任意位置满足【解题报告】CF DIV2 #ROUND 723 A~D_逆序对_02
思路
先排序,然后
结果我憨憨的写sort只写了前n个,WA掉一发呜呜呜
代码

// Problem: A. Mean Inequality
// Contest: Codeforces - Codeforces Round #723 (Div. 2)
// URL: https://codeforces.com/contest/1526/problem/A
// Memory Limit: 256 MB
// Time Limit: 1000 ms
// FishingRod

#include<bits/stdc++.h>
using namespace std;
#define endl "\n"
typedef long long LL;
typedef pair<int,int> PII;
#define MULINPUT
/*DATA & KEY

*/
int T;
const int N=55;
LL a[N],b[N];
void solve(int C)
{
//NEW DATA CLEAN

//NOTE!!!
int n;cin>>n;
for(int i=1;i<=2*n;i++)cin>>a[i];
sort(a+1,a+1+2*n);
for(int i=1,j=1;i<=n;i++,j+=2)b[j]=a[i];
for(int i=2*n,j=2;i>=n+1;i--,j+=2)b[j]=a[i];
for(int i=1;i<=2*n;i++)cout<<b[i]<<" ";
puts("");
}

int main()
{
#ifdef MULINPUT
scanf("%d",&T);
for(int i=1;i<=T;i++)solve(i);
#else
solve(1);
#endif
return 0;
}

B. I Hate 1111

题意
问一个数能不能由11,111,1111……构造出来
思路
可以发现1111,11111……都可以由11,111构造出来,所以只需要考虑能否被11和111构造出来。
也就是【解题报告】CF DIV2 #ROUND 723 A~D_#define_03,要有非负整数解
有定理:可以看一下这题 ​​小凯的疑惑​​【解题报告】CF DIV2 #ROUND 723 A~D_#define_04
也就是最大不能凑出1,099。
然后直接枚举一波<1099能凑出的数即可

代码

#include<bits/stdc++.h>
using namespace std;
map<int,bool>mp;
int main(){
int n;cin>>n;
for(int i=0;i<=100;i++)
for(int j=0;j<=10;j++)
mp[i*11+j*111]=1;
for(int i=1;i<=n;i++){
int x;cin>>x;
if(x>1099||mp[x])puts("YES");
else puts("NO");
}
return 0;
}

大佬的奇妙数论操作

// Problem: B. I Hate 1111
// Contest: Codeforces - Codeforces Round #723 (Div. 2)
// URL: https://codeforces.com/contest/1526/problem/B
// Memory Limit: 256 MB
// Time Limit: 1000 ms
// FishingRod

#include<bits/stdc++.h>
using namespace std;
#define endl "\n"
typedef long long LL;
typedef pair<int,int> PII;
#define MULINPUT
/*DATA & KEY

*/
int T;
void solve(int C)
{
//NEW DATA CLEAN

//NOTE!!!
LL x;cin>>x;
if(x%11<=x/11/10)puts("YES");
else puts("NO");
}

int main()
{
#ifdef MULINPUT
scanf("%d",&T);
for(int i=1;i<=T;i++)solve(i);
#else
solve(1);
#endif
return 0;
}

C1+C2 Potions

题意
有n瓶药水,对应值a[i],a[i]可正可负,范围正负1e9,对于每瓶药水你可以选择喝或者不喝,但是必须保证选择药水的和非负,问最多喝多少瓶。
思路
C1和C2只是n数据量不同,我直接写通解了。
主要是反悔贪心和单调队列的思想,个人是用优先队列实现的。
​反悔贪心看一看这一篇文章​​ 首先有一个显然的贪心思想就是,只要sum不会0,那就喝下药水但是这样其实是不ok的。

10 -10 -1 -2 -3

显然我们可以选择不喝-10,喝后面3瓶。那么,如何实现这一操作呢。
答案是反悔贪心。
反悔贪心主要的思想是替换和做差。
首先我们一直喝,直到继续喝下去sum可能<0。
然后我们就要思考当前x能否替换掉先前喝过的值最小的药水。
替换的条件有以下2个
①替换后sum>=0
②x比替换的值大
也就是if(sum-heap.top()+x>=0&&heap.top()<x)
代码

// Problem: C1. Potions (Easy Version)
// Contest: Codeforces - Codeforces Round #723 (Div. 2)
// URL: https://codeforces.com/contest/1526/problem/C1
// Memory Limit: 256 MB
// Time Limit: 1000 ms
// FishingRod

#include<bits/stdc++.h>
using namespace std;
#define endl "\n"
typedef long long LL;
typedef pair<int,int> PII;
//#define MULINPUT
/*DATA & KEY

*/
int T;
void solve(int C)
{
//NEW DATA CLEAN
priority_queue<LL,vector<LL>,greater<LL>>heap;
//NOTE!!!
int n;scanf("%d",&n);
LL sum=0;
for(int i=1;i<=n;i++)
{
LL x;scanf("%lld",&x);
if(heap.empty())//如果空
{
if(x>=0)//正的直接加入
{
heap.push(x);
sum+=x;
}
}
else
{
if(x>=0)//正的直接加入
{
heap.push(x);
sum+=x;
}
else//x<0
{
if(sum+x>=0)//可以喝直接加入
{
sum+=x;
heap.push(x);
}
else
{
if(sum-heap.top()+x>=0&&heap.top()<x)//满足替换条件,我们就把堆顶的药水换掉
{
sum=sum-heap.top()+x;
heap.pop();
heap.push(x);
}

}

}
}
}
cout<<heap.size()<<endl;
}

int main()
{
#ifdef MULINPUT
scanf("%d",&T);
for(int i=1;i<=T;i++)solve(i);
#else
solve(1);
#endif
return 0;
}

D.Kill Anton

【解题报告】CF DIV2 #ROUND 723 A~D_#define_05

题意

给你一个字符串,你要对它的字母重新排列。排列后的字符串只能通过相邻位置两两交换的方式回到原串。请你构造一个排列方式,使得它变回原串的操作次数最多。

思路

相邻交换的话,联想到冒泡排序和逆序对数量,每交换一次逆序对增加一次,也就是说让映射过去的逆序对数最多

emmm然后蒟蒻鱼竿就卡在这里了。

问了大佬,大佬说有个奇妙的结论:相同字母一定是连续的

然后直接枚举【解题报告】CF DIV2 #ROUND 723 A~D_#define_06种排列可能,求逆序对,记录最大的字符串即可。

【解题报告】CF DIV2 #ROUND 723 A~D_i++_07

代码

// Problem: D. Kill Anton
// Contest: Codeforces - Codeforces Round #723 (Div. 2)
// URL: https://codeforces.com/contest/1526/problem/D
// Memory Limit: 512 MB
// Time Limit: 2000 ms
// FishingRod

#include<bits/stdc++.h>
using namespace std;
#define endl "\n"
typedef long long LL;
typedef pair<int,int> PII;
#define MULINPUT
/*DATA & KEY

*/
int T;
const int N=2E5+10;
int num[N],sort_tmp[N];
int get_id(char c)//获取对应id
{
if(c=='A')return 0;
if(c=='N')return 1;
if(c=='T')return 2;
if(c=='O')return 3;
}
LL merge_sort(int l,int r)//归并求逆序对
{
if(l>=r)return 0;
int mid=l+r>>1;
LL res=merge_sort(l,mid)+merge_sort(mid+1,r);
int k=0,i=l,j=mid+1;//i是l别打成1
while(i<=mid&&j<=r)
{
if(num[i]<=num[j])sort_tmp[k++]=num[i++];
else
{
sort_tmp[k++]=num[j++];
res+=mid-i+1;//q[i]>q[j],左区间剩下的所有数与右区间当前数成为逆序对
}
}
while(i<=mid)sort_tmp[k++]=num[i++]; //扫尾
while(j<=r)sort_tmp[k++]=num[j++];
for(int i=l,j=0;i<=r;i++,j++)num[i]=sort_tmp[j];//不要写成i=1
return res;
}
LL get_rev(string s,int* p){//求字符串逆序对数量
int len=s.length();
for(int i=0;i<len;i++)
for(int j=0;j<4;j++)
if(get_id(s[i])==p[j])//根据排序比较规则p[j],把string映射成数组求逆序对
{
num[i]=j;//下标j就是rank
break;
}
return merge_sort(0,len-1);
}
void solve(int C)
{
//NEW DATA CLEAN
//NOTE!!!
int p[]={0,1,2,3};//排列顺序,同时也是求逆序对数的排序比较规则
string s;cin>>s;
string ans,A,N,T,O;
for(int i=0;s[i];i++)
{
if(s[i]=='A')A+='A';
if(s[i]=='N')N+='N';
if(s[i]=='T')T+='T';
if(s[i]=='O')O+='O';
}
LL rev=0;
do{
string tmp;
for(int i=0;i<4;i++){
if(p[i]==0)tmp+=A;
if(p[i]==1)tmp+=N;
if(p[i]==2)tmp+=T;
if(p[i]==3)tmp+=O;
}
LL k=get_rev(s,p);
if(k>=rev)
{
rev=k;
ans=tmp;
}
}while(next_permutation(p,p+4));
cout<<ans<<endl;
}

int main()
{
#ifdef MULINPUT
scanf("%d",&T);
for(int i=1;i<=T;i++)solve(i);
#else
solve(1);
#endif
return 0;
}

反思

A:

对于成环的,使用sort或者其他东西的时候一定要注意2n而不是n

B:

问能否用给出的数构造询问的数思考以下两个
【解题报告】CF DIV2 #ROUND 723 A~D_#define_08
可以用来缩小枚举范围
给出的数之间能否相互包含相互构造

C:

反悔贪心流程
①普通贪心直到边界条件,并记录堆的相关数据
②看当前要加入数x替换堆顶元素后是否优于原来的,并且满足边界条件,并记录相关数据

如何实现替换
利用做差等操作维护堆的数据信息,堆顶的弹出,新数据插入堆

D:

相邻交换,联想到冒泡排序和逆序对