Codeforces Round #887 Div.2 A-F
Codeforces Round #887 Div.2 一定要手玩哦
前言:
一定要手玩,一定要手玩!
我今早一手玩发现C很TM简单,如果赛时我能切C说不定直接上1800.。。。
时隔多年,我的Codeforces Rating(1718) 再次超越了 @cqbzlhy(1674)!!!
赛时错误:
- B题出得太慢了->劣于pzj半小时。%%%pzj
- C没有手玩,瞪眼半天看不出来
- D由于特判问题RE了两发!!!!!(还好不是WA不计罚时)
这次怎么DF全是构造啊。。。。。
要想题目考思维,数学构造加几何!
A-Desorting
简单题。找到差的最小值,将其除以2再加一就是答案。注意负数直接输出0.
B-Fibonaccharsis
题意:给定
有意思,设
注意到可以枚举该类斐波那契数列的第一项,则第二项显然是唯一的且具有单调性的。二分查找即可。
斐波那契数列增长非常快,
if(n==0){
cout<<"1\n";continue;
}
if(k>35){
cout<<"0\n";continue;
}
int ans=0;
for(int i=0;i<=n;i++){
int l=i,r=n-i;
while(l<r){
int j=l+r>>1;
int a=i,b=j,tag=0;
for(int p=3;p<=k;p++){
int z=a+b;
if(p==k){
if(z>=n)r=j;
else l=j+1;
}
if(z>n){
r=j;break;
}
a=b,b=z;
}
}
int j=l;
int a=i,b=j,tag=0;
for(int p=3;p<=k;p++){
int z=a+b;
if(p==k&&z==n){
ans++;
}
if(z>n){
break;
}
a=b,b=z;
}
}
cout<<ans<<"\n";
事实上,这不够数学。
注意到类斐波那契数列
C-Ntarsis'Set
题意:
给定序列
由于我们只求最小值,所以先只关心最小值的变化情况。
例如在
观察多组数据,容易发现在
也即
时间复杂度:
边界:
#include<iostream>
using namespace std;
int a[505050],n,t,k;
int main(){
ios::sync_with_stdio(false);
cin>>t;
while(t--){
cin>>n>>k;
for(int i=1;i<=n;i++)cin>>a[i];
int x=0,d=0;
while(k--){
while(d+1<=n&&a[d+1]<=x+d+1)++d;
x+=d;
}
cout<<x+1<<"\n";
}
}
学到的东西:
找规律问题可以关注
手玩几组样例,明确关注点。
D-Imbalanced Arrays
要求构造一个长为
题解:注意到,若
那么只需要考虑条件3。注意到
这时候我们来考虑一个数为正数的条件:
那么
当我们确定完正数的值的时候,负数的值就把排列里剩下的数按大小关系分配即可(这个数可以算出来,用来判无解)。
最后判一下无解。
注意特判:当
struct node{
int id,x;
bool operator<(const node b){
return x<b.x;
}
}a[505050];
int b[505050],cnt[505050],tot,vis[505050];
int main(){
int t=read(),n;
while(t--){
for(int i=1;i<=n;i++)vis[i]=0;
n=read();tot=0;
for(int i=1;i<=n;i++)a[i].x=read(),a[i].id=i;
sort(a+1,a+n+1);
int x=-1;
for(int i=1;i<=n;i++){
if(a[i].x>=n-i+1){
x=i;
for(int j=i;j<=n;j++){
int p=a[j].x;p-=n-i+1;p+=j-i+1;vis[p]=1;
b[a[j].id]=p;
}
break;
}
}
if(x==-1){
cout<<"YES\n";for(int i=1;i<=n;i++)cout<<"-1 ";cout<<"\n";continue;
}
int j=1,k=0;
for(int i=x-1;i;i--){
while(vis[j]&&j<=n)++j,++k;
b[a[i].id]=-j;
if(n-x+1-k!=a[i].x){
b[a[i].id]=-n-1;
}
vis[j]=1;--k;
}
int tag=0;
for(int i=1;i<=n;i++){
if(b[i]>n||b[i]<-n){
cout<<"NO\n";tag=1;break;
}
}
if(tag)continue;
cout<<"YES\n";
for(int i=1;i<=n;i++)cout<<b[i]<<" ";cout<<"\n";
}
}
E-Ina of the Moutain
题意:给定数
首先,让我们思考一下如果没有这个模
设
那么我们考虑这个问题,逆向思考。
首先,假设我们已经知道了最优方案下每一个
让我们再来思考一些性质。直觉告诉我们必定存在最优方案使得每一个
那么我们来考虑计算答案。
不直观的序列问题,可以转化到平面上,尤其是做差求值,在平面上可以直接通过坐标差体现,转化为路径。
那么每个
我们将
这里的路径长度定义为:
盗个图,例如
这里是这个题最魔幻的部分,也是最让人着迷的地方(w看了一个下午才懂。。。。)
那么这里显然有贪心策略:能往下走就往下走,走不下去了就往上爬。设走不下去的点为
但,这个往上爬是有技术含量的。可以在之前的某个位置
那么我们就是要找到这个向上爬的最小代价。显然我们可以通过优先队列维护每一个向上爬的路。(对于每一个
代码是那么地简洁与优雅,令人着迷。
#define int long long
int n,t,k;
signed main(){
ios::sync_with_stdio(false);
cin>>t;
while(t--){
cin>>n>>k;
int lst=0,ans=0;priority_queue<int>q;
for(int i=1;i<=n;i++){
int x;cin>>x;x%=k;
if(x>lst){
q.push(-(x-lst));
ans-=q.top();
q.pop();
}
else q.push(-(k-lst+x));
lst=x;
}
cout<<ans<<"\n";
}
}
F-Miriany and Matchstick
题意:有一个
不同相邻对指两个点
动态规划维护若干答案区间,倒推获取方案
其实比较简单吧这个题。。。。首先,我们要发现一个性质:
在一个方案中,将一个字符(非边界)改为另一个字符,带来的影响是其不同相邻对的值变化1/3,而边界则变化0/2。
在边界确定的情况下,容易发现,合法的
答案在固定区间范围内,答案可拼凑性,区间有限性,共同解决了本题维护答案区间的思想。
我们考虑枚举第一个数填A/B,分别计算出其答案范围,不妨记为
那么显然有:
f[i][0]=add(merge(f[i-1][0],add(f[i-1][1],1)),(s[i]!=s[i-1])+(s[i]!='A'));
f[i][1]=add(merge(f[i-1][1],add(f[i-1][0],1)),(s[i]!=s[i-1])+(s[i]!='B'));
其中
然后我们考虑计算答案。用函数
那么倒推回去计算方案,
#include<bits/stdc++.h>
using namespace std;
#define pr pair<int,int>
#define l first
#define r second
#define mk make_pair
struct node{
vector<pr >a;
};
node add(node x,int d){
for(int i=0;i<x.a.size();i++){
x.a[i].l+=d,x.a[i].r+=d;
}
return x;
}
node merge(node x,node y){
node c;c.a.clear();//merge(x.a.begin(),x.a.end(),y.a.begin(),y.a.end(),c.a);
int n=0,m=0;
while(n<x.a.size()&&m<y.a.size()){
if(x.a[n]<y.a[m]){
c.a.push_back(x.a[n]);++n;
}
else c.a.push_back(y.a[m]),++m;
}
while(n<x.a.size())c.a.push_back(x.a[n]),++n;
while(m<y.a.size())c.a.push_back(y.a[m]),++m;
int j=0;
for(int i=0;i<c.a.size();i++){
if(c.a[j].r+1>=c.a[i].l){
c.a[j].r=max(c.a[j].r,c.a[i].r);
}else c.a[++j]=c.a[i];
}
c.a.erase(c.a.begin()+j+1,c.a.end());
return c;
}
bool exist(node x,int k){
for(int i=0;i<x.a.size();i++){
int l=x.a[i].l,r=x.a[i].r;
if(l<=k&&k<=r)return true;
}
return false;
}//区间操作
int t,n,k;
char s[505050];
node f[505050][2];
int main(){
ios::sync_with_stdio(false);cin>>t;
while(t--){
cin>>n>>k;
for(int i=0;i<n;i++)f[i][1].a.clear(),f[i][0].a.clear();
cin>>s;
f[0][0].a.push_back(mk(s[0]!='A',s[0]!='A'));
f[0][1].a.push_back(mk(s[0]!='B',s[0]!='B'));
for(int i=1;i<n;i++){
f[i][0]=add(merge(f[i-1][0],add(f[i-1][1],1)),(s[i]!=s[i-1])+(s[i]!='A'));
f[i][1]=add(merge(f[i-1][1],add(f[i-1][0],1)),(s[i]!=s[i-1])+(s[i]!='B'));
}
if(exist(f[n-1][0],k)==false&&exist(f[n-1][1],k)==false){
cout<<"No\n";continue;
}
string ans="";
for(int i=n-1;i>=0;--i){
int w=exist(f[i][0],k-(i==n-1?0:(ans.back()!='A')));
char x=w?'A':'B';
k-=(x!=s[i]);
if(i!=n-1)k-=(x!=ans.back());
if(i!=0)k-=(s[i]!=s[i-1]);
ans+=x;
}
reverse(ans.begin(),ans.end());
cout<<"Yes\n"<<ans<<"\n";
}
}
区间个数少是突破点。
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 在鹅厂做java开发是什么体验
· 百万级群聊的设计实践
· WPF到Web的无缝过渡:英雄联盟客户端的OpenSilver迁移实战
· 永远不要相信用户的输入:从 SQL 注入攻防看输入验证的重要性
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析