写在前面:
作者参加了省赛,在场上写出了五道题,最后A题没有做出来遗憾没拿银,在这里记录一下自己的题解和补一下A题,补题链接:https://codeforces.com/gym/106532。
F.晚上早
思路:
直接遍历24个小时,如果24小时都不能发送消息的话就永远不能发送消息,输出-1。
代码:
cpp
#include<bits/stdc++.h>
using namespace std;
using ll=long long;
void solve()
{
ll d,t0;
cin>>d>>t0;
for(ll i=0;i<=24;i++)
{
ll t1=t0+i;//小A
if(t1>23)t1%=24;
ll t2=(t1+d)%24;//小E
if(t1>=6&&t1<=17){
if(t2<=5||t2>=18){
cout<<i<<endl;
return ;
}
}
}
cout<<-1<<endl;
}
int main(){
ll _=1;
// cin>>_;
while(_--){
solve();
}
return 0;
}
L.旧歌谣
思路:
最高位变化两次即可,分两种情况,
1、看最高位是不是一开头,例如x=150需要播放到x=89为止即余数50+小一位单位(10)+1
2、而最高位不是一开头的例如350就遍历到199。即余数50+同级单位(100)+1
代码:
cpp
#include<bits/stdc++.h>
using namespace std;
using ll=long long;
void solve()
{
ll x;
cin>>x;
ll temp=x;
ll wei=0;
while(temp>=10){
temp/=10;
wei++;
}
ll ans=0;
ll zheng=(ll)pow(10,wei);
if(temp==1){
ans=x%zheng;
zheng/=10;
ans=ans+zheng+1;
}
else{
ans=x%zheng;
ans=ans+zheng+1;
}
cout<<ans<<endl;
}
int main(){
ll _=1;
// cin>>_;
while(_--){
solve();
}
return 0;
}
G.Wanna Be Free
思路:
报销串要么是以B开头然后交替出现,要么以A开头交替出现,所以直接把这两种目标串构建出来,然后和原串对比,计和原串字符不同的最左端和最右端,就是最短所需子串,再把这两种情况对比一下就行。
代码:
cpp
#include<bits/stdc++.h>
using namespace std;
using ll=long long;
void solve()
{
string s;
cin>>s;
ll n=s.size();
string s1="";
string s2="";
for(ll i=1;i<=n;i++){
if(i%2==1){
s1+="B";
}
else{
s1+="W";
}
}
for(ll i=1;i<=n;i++){
if(i%2==1){
s2+="W";
}
else{
s2+="B";
}
}
ll left=0,right=0;
bool flag=0;
for(ll i=0;i<n;i++){
if(s[i]!=s1[i]){
if(flag==0){
flag=1;
left=i;
}
right=i;
}
}
if(flag==0){
cout<<0<<endl;
return ;
}
ll ans=right-left+1;
left=0,right=0,flag=0;
for(ll i=0;i<n;i++){
if(s[i]!=s2[i]){
if(flag==0){
flag=1;
left=i;
}
right=i;
}
}
if(flag==0){
cout<<0<<endl;
return ;
}
ans=min(right-left+1,ans);
cout<<ans<<endl;
}
int main(){
ll _=1;
// cin>>_;
while(_--){
solve();
}
return 0;
}
E.奔向天际线
思路:
观察发现必须要更新使用过的传送门状态,所以不好用O(logn)或者O(1)的方法,根据数据来看,如果传送门强度较大时,传送次数少直接模拟不会超时,如果传送门强度较小,很快就会到强度为1,此时并不会超时,但继续模拟强度为1的传送会超时,故使用压缩的方法,如果有连续的1直接跳到最右边即可。这里可以用并查集,我在赛场上的时候没用板子,但实现思想差不多。
代码:
cpp
#include<bits/stdc++.h>
using namespace std;
using ll=long long;
ll n,m;
ll dfs(ll x,vector<ll>&a,vector<ll>&mubiao){//x为当前传送门,a为传送门强度数组,mubiao值如果不为0就是传送门强度为0的连续片段最右端点。
if(a[x]!=1||x==n+1){
return x;
}
if(mubiao[x]!=0){
mubiao[x]=dfs(mubiao[x],a,mubiao);
}
else{
mubiao[x]=dfs(x+1,a,mubiao);
}
return mubiao[x];
}
void solve()
{
cin>>n>>m;
vector<ll>a(n+1);
vector<ll>mubiao(n+1);
for(ll i=1;i<=n;i++)cin>>a[i];
while(m--){
ll b;
cin>>b;
ll cnt=0;
while(1){
if(a[b]!=1){
cnt++;
ll temp=a[b];
a[b]--;
b+=temp;
if(b>n){
cout<<cnt<<endl;
break;
}
}
else{
ll r=dfs(b,a,mubiao);
cnt+=(r-b);
b=r;
if(b>n){
cout<<cnt<<endl;
break;
}
}
}
}
}
int main(){
ll _=1;
// cin>>_;
while(_--){
solve();
}
return 0;
}
H.峰终定律
思路:
因为最后一个数必定会被选为一个子序列的最右边界,所以单独提出来处理。
先将除开最后一个数的序列排序,用结构体记下他们的值和下标,遍历这个序列,如果值为正,每次都选最大的两个值构成子序列,如若为负就将上一个子序列里面的正数移到这个子序列里面来,再将所有负数和最后一位数都加到这个子序列(例如上个为5 4,这个为-3 -2 -1 5,将4移过来能够值最大化)。因为边界的一些问题,我特判了一些条件。
代码:
cpp
#include<bits/stdc++.h>
using namespace std;
using ll=long long;
struct node{
ll v;
ll i;
};
struct cmp{
bool operator()(node a,node b){
return a.v>b.v;
}
}Cmp;
void solve()
{
ll n,k;
cin>>n>>k;
vector<node>a(n);
vector<ll>b(n+1);
for(ll i=1;i<n;i++)
{
cin>>a[i].v;
a[i].i=i;
}
ll last_num=0;
cin>>last_num;
sort(a.begin()+1,a.end(),Cmp);
ll ans=0;
if(a[1].v<=0||k==1)//特判全为负数的情况或者k=1
{
ans+=a[1].v;
ans+=last_num;
cout<<ans<<endl;
for(ll i=1;i<=n;i++)
{
cout<<1<<" ";
}
return ;
}
ll i=1;
while(i<=n-1){
if(k>1){
if(i==n-1){//防止i+1=n的时候越界
if(a[i].v>0){
b[a[i].i]=k;
b[n]=k;
ans+=last_num;
ans+=a[i].v;
break;
}
else{
b[a[i].i]=k;
b[a[i-1].i]=k;
b[n]=k;
ans+=last_num;
break;
}
}
if(a[i].v>0){
if(a[i+1].v>0){
b[a[i].i]=k;
b[a[i+1].i]=k;
k--;
ans+=a[i].v;
ans+=a[i+1].v;
i+=2;
continue;
}
else{
for(ll j=i;j<=n-1;j++)
{
b[a[j].i]=k;
}
b[n]=k;
ans+=a[i].v;
ans+=last_num;
break;
}
}
else{
for(ll j=i;j<=n-1;j++)
{
b[a[j].i]=k;
}
b[a[i-1].i]=k;
b[n]=k;
ans+=last_num;
break;
}
}
else{//不能再额外有子序列了,直接把剩下的元素处理掉
if(a[i].v>0){
for(ll j=i;j<=n-1;j++)
{
b[a[j].i]=k;
}
b[n]=k;
ans+=a[i].v;
ans+=last_num;
break;
}
else{
for(ll j=i;j<=n-1;j++)
{
b[a[j].i]=k;
}
b[n]=k;
b[a[i-1].i]=k;
ans+=last_num;
break;
}
}
}
if(b[n]==0)//没有break,刚好i>=n-1跳出while语句的时候最后一位并没有处理
{
b[n]=1;
ans+=last_num;
}
cout<<ans<<endl;
for(ll i=1;i<=n;i++)
{
cout<<b[i]<<" ";
}
}
int main(){
ll _=1;
// cin>>_;
while(_--){
solve();
}
return 0;
}
A.无法告别的梦境
这道题明天再继续写了
思路:
忘记关IO流,q较大的时候t了一发(难崩)
先预处理
显而易见从左边点到右边点一定会经过x2-x1个木板,所以需要看经过这个数目的木板在不额外经过纵向木板情况下最大到边界哪里(这里使用了前缀的思路优化时间复杂度),能覆盖目标点就不需要额外经过木板,否则需要纵向再经过木板,这里分是否都在最下方那块木板上(我直接mod==0来判断特殊处理了)
此外因为初始值是小数,所以需要都等效优化到木板边界上。
代码:
cpp
#include<bits/stdc++.h>
using namespace std;
using ll=long long;
void solve()
{
ll n,k,q;
cin>>n>>k>>q;
vector<ll>a(n+1);
for(ll i=1;i<=n;i++)cin>>a[i];
vector<ll>preu(n+1);
vector<ll>pred(n+1);//前缀为向右移动时候不额外进入木板能移动的距离
for(ll i=2;i<=n;i++)
{
preu[i]=preu[i-1]+(a[i]-a[i-1]+k)%k;
pred[i]=pred[i-1]+(a[i-1]-a[i]+k)%k;
}
while(q--){
ll x1,y1,x2,y2;
cin>>x1>>y1>>x2>>y2;
if(x1>x2){
swap(x1,x2);
swap(y1,y2);
}
ll ans=x2-x1;
if(y1<y2){
double temp=y1-0.5;
ll temp1=0;
if(y1-0.5<a[x1]){
y1=a[x1];
}
else{
temp-=a[x1];
temp1=(ll)(temp/k);
temp1++;
y1=a[x1]+temp1*k; //移动到木板上方
}
if(y2-0.5<a[x2]){
y2=a[x2];
}
else{
temp=y2-0.5;
temp-=a[x2];
temp1=(ll)(temp/k);
temp1++;
y2=a[x2]+temp1*k;
}
//移动到同一列的最大高度
y1+=(preu[x2]-preu[x1]);
if(y1>=y2){
cout<<ans<<endl;
continue;
}
else{
ans+=(y2-y1)/k;
if((y2-y1)%k!=0){
ans++;
}
cout<<ans<<endl;
continue;
}
}
else{
double temp=y1-0.5;
ll temp1=0;
if(y1-0.5<a[x1]){
y1=0;
}
else{
temp-=a[x1];
temp1=(ll)(temp/k);
temp1++;
y1=a[x1]+temp1*k; //移动到木板下方
y1-=k;
}
if(y2-0.5<a[x2]){
y2=0;
}
else{
temp=y2-0.5;
temp-=a[x2];
temp1=(ll)(temp/k);
temp1++;
y2=a[x2]+temp1*k;
y2-=k;
}
//移动到同一列的最小高度
y1-=(pred[x2]-pred[x1]);
// cout<<y1<<" "<<y2<<" "<<ans<<endl;
if(y1<=y2){
cout<<ans<<endl;
continue;
}
else{
ans+=(y1-y2)/k;
if((y1-y2)%k!=0){
ans++;
}
cout<<ans<<endl;
continue;
}
}
}
}
int main(){
ios_base::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
ll _=1;
// cin>>_;
while(_--){
solve();
}
return 0;
}