链接:Codeforces Round #547 (Div. 3)
A. Game 23
题意:
给a和b,a可以选择a*2或者a*3来变成b,问最少几步变成b,变不出输出-1
先判断b%a能否=0,c=b/a,c变成1的最小步数
ac:
#include<bits/stdc++.h>
#define IOS std::ios::sync_with_stdio(false);
#define pb push_back
#define ll long long
#define mod 1000000007
#define per(i,a,b) for(int i=a;i<b;i++)
#define rep(i,a,b) for(int i=a;i>=b;i--)
using namespace std;
int main()
{
int a,b;
cin>>a>>b;
int c=b/a;
int d=b%a;
if(d!=0||b<a)
{
printf("-1\n");
return 0;
}
int cnt=0;
while(c%2==0)
{
c=c/2;
cnt++;
}
while(c%3==0)
{
c=c/3;
cnt++;
}
if(c!=1)
printf("-1\n");
else printf("%d\n",cnt);
return 0;
}
B. Maximal Continuous Rest
题意:
给一天每小时的休息情况,问最多休息时间,必休息一次
解析:
开两倍数组遍历
ac:
#include<bits/stdc++.h>
#define MAXN 400005
using namespace std;
int a[MAXN]={0};
int main()
{
int n;
scanf("%d",&n);
for(int i=0;i<n;i++)
{
scanf("%d",&a[i]);
a[n+i]=a[i];
}
int maxs=0;
for(int i=0;i<2*n;i++)
{
if(a[i]==0) continue;
a[i]=a[i-1]+a[i];
maxs=max(maxs,a[i]);
}
printf("%d\n",maxs);
return 0;
}
C. Polycarp Restores Permutation
题意:
qi=pi+1−pi,给(1~n-1)的qi,求(1~n)的pi
解析:
给pn赋值400005,从后往前推
找(1~n)里pi最小的,每个pi减去(mins-1),就一定有个pi等于1
遍历pi,判断是否超过n或者重复
ac:
#include<bits/stdc++.h>
#define ll long long
#define MAXN 500005
using namespace std;
int a[MAXN];
int b[MAXN];
map<ll,int> vis;
int main()
{
int n;
scanf("%d",&n);
for(int i=1;i<n;i++)
scanf("%d",&a[i]);
int c=400005;
b[n]=c;
int maxs=b[n];
for(int i=n-1;i>=1;i--)
{
b[i]=b[i+1]-a[i];
maxs=min(b[i],maxs);
}
maxs--;
for(int i=1;i<=n;i++)
{
b[i]=b[i]-maxs;
if(vis[b[i]]==1||(b[i]>n))
{
printf("-1\n");
return 0;
}
vis[b[i]]=1;
}
for(int i=1;i<n;i++)
printf("%d ",b[i]);
printf("%d\n",b[n]);
return 0;
}
另外一种差不多的写法
#include<cstdio>
#include<iostream>
#include<vector>
#include<algorithm>
#include<cstring>
#include<set>
using namespace std;
typedef long long LL;
const int Max_n=200005;
int a[Max_n],b[Max_n];
set<int> st;
int main()
{
int n;
scanf("%d",&n);
int mmin=Max_n;
int mmax=-Max_n;
for(int i=2;i<=n;i++)
{
scanf("%d",&a[i]);
b[i]=b[i-1]+a[i];
}
for(int i=1;i<=n;i++)
{
st.insert(b[i]);
mins=min(mins,b[i]);
maxs=max(maxs,b[i]);
}
int len=st.size();
if(len!=n||maxs-mins!=n-1)//st自动去重复,如果有重复,len!=n
{ //mmax-min!=n-1,会有pi超过n
printf("-1\n");
return 0;
}
for(int i=1;i<=n;i++)
printf("%d%c",b[i]-mins+1,i==n?'\n':' ');
return 0;
}
D. Colored Boots
题意:
给定a串b串,里面有?,?可以变任何字母
求a串和b串字母个数相同的最多数目
解析:
直接模拟就可以了,先比a~z,在a~z和?匹配,如果?有多,?和?匹配
ac:
#include<bits/stdc++.h>
#define pb push_back
#define MAXN 500000
using namespace std;
char str[MAXN];
char ctr[MAXN];
queue<int> vca[100],vcb[100];
int aa[MAXN],bb[MAXN];
int main()
{
int cnt=0;
int n;
scanf("%d",&n);
scanf("%s",str+1);
scanf("%s",ctr+1);
for(int i=1;i<=n;i++)
{
if(str[i]=='?')
vca[30].push(i);
else vca[str[i]-'a'+1].push(i);
if(ctr[i]=='?')
vcb[30].push(i);
else vcb[ctr[i]-'a'+1].push(i);
}
for(int i=1;i<=26;i++)
{
while(vca[i].size()>0&&vcb[i].size()>0)
{
aa[cnt]=vca[i].front();
bb[cnt]=vcb[i].front();
cnt++;
vca[i].pop();
vcb[i].pop();
}
}
if(vca[30].size()>0)
{
for(int i=1;i<=26;i++)
{
while(vcb[i].size()>0&&vca[30].size()>0)
{
aa[cnt]=vca[30].front();
bb[cnt]=vcb[i].front();
cnt++;
vcb[i].pop();
vca[30].pop();
}
}
}
if(vcb[30].size()>0)
{
for(int i=1;i<=26;i++)
{
while(vca[i].size()>0&&vcb[30].size()>0)
{
aa[cnt]=vca[i].front();
bb[cnt]=vcb[30].front();
cnt++;
vca[i].pop();
vcb[30].pop();
}
}
}
while(vca[30].size()>0&&vcb[30].size()>0)
{
aa[cnt]=vca[30].front();
bb[cnt]=vcb[30].front();
cnt++;
vca[30].pop();
vcb[30].pop();
}
printf("%d\n",cnt);
for(int i=0;i<cnt;i++)
printf("%d %d\n",aa[i],bb[i]);
return 0;
}
E. Superhero Battle
给定怪的血量
英雄每回合的伤害,伤害可能为正(加血),回合是循环的
问最少几回合打死野怪,打不死输出-1
解析:
前缀和+二分
前缀和求sum[i](1~k),求里面最小的
二分找最小的q,n-q*sum[k]+mins<=0
要注意q的范围,这题数据很严,直接赋r过大会造成q*sum[k]+mins溢出ll的负下限,我们先求r的不溢出上限
解析:
#include<bits/stdc++.h>
#define MAXN 3000005
#define ll long long
using namespace std;
ll a[MAXN];
ll sum[MAXN];
int main()
{
ll n,k;
scanf("%lld%lld",&n,&k);
for(ll i=1;i<=k;i++)
scanf("%lld",&a[i]);
sum[1]=a[1];
for(ll i=2;i<=k;i++)
sum[i]=sum[i-1]+a[i];
ll mins=999999999999999;
for(ll i=1;i<=k;i++)
{
mins=min(mins,sum[i]);
if(n+sum[i]<=0)
{
printf("%lld\n",i);
return 0;
}
}
if(sum[k]>=0)
{
printf("-1\n");
}
else{
ll l=1,r=(9e18+mins)/(-1*sum[k]);//防止sum[k]*mid+mins越界
ll q=r;
while(l<=r)
{
ll mid=(l+r)/2;
if(n+sum[k]*mid+mins<=0)
{
r=mid-1;
q=min(q,mid);
}
else{
l=mid+1;
}
}
n=n+q*sum[k];
for(ll i=1;i<=k;i++)
{
if(n+sum[i]<=0)
{
printf("%lld\n",q*k+i);
break;
}
}
}
return 0;
}
F2. Same Sum Blocks (Easy)
题意
给一个数组,
求一些不相交的区间,每个区间的和为k,求区间的最大数目
解析:
先求前缀和
两层循环遍历区间,将相同区间和的区间装入vector中
最后遍历每一个区间,贪心求最多区间数目
ac:
#include<bits/stdc++.h>
#define ll long long
#define MAXN 3000005
using namespace std;
struct node
{
int x,y;
bool friend operator <(node a,node b)
{
if(a.y==b.y)
return a.x<b.x;
else return a.y<b.y;
}
};
int a[MAXN];
int sum[MAXN];
map<ll,int> mp;
vector<node> vc[MAXN];
int cnt;
int main()
{
//freopen("E:/in.txt","r",stdin);
cnt=1;
int n;
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
sum[i]=sum[i-1]+a[i];
}
for(int i=1;i<=n;i++)
{
for(int j=i;j<=n;j++)
{
int cc=sum[j]-sum[i-1];
if(mp[cc]==0)
{
mp[cc]=cnt;
vc[cnt].push_back({i,j});
cnt++;
}
else{
vc[mp[cc]].push_back({i,j});
}
}
}
int maxs=0,qc,ans=0;
for(int i=1;i<cnt;i++)
{
int len=vc[i].size();
if(len<ans)
continue;
int r=0,sum=0;
sort(vc[i].begin(),vc[i].end());
for(int j=0;j<vc[i].size();j++)
{
if(vc[i][j].x>r)
{
r=vc[i][j].y;
sum++;
}
}
if(sum>ans)
{
ans=sum;
qc=i;
}
}
printf("%d\n",ans);
int r=0;
for(int i=0;i<vc[qc].size();i++)
{
if(vc[qc][i].x>r)
{
printf("%d %d\n",vc[qc][i].x,vc[qc][i].y);
r=vc[qc][i].y;
}
}
return 0;
}
https://codeforces.com/contest/1256
题意:
B. Minimize the Permutation
题意:
给定一个数组,让你选择两组相邻元素交换,(i,i+1),每个位置只能选择一次
解析:
贪心,从小到大选择,并标记
ac:
#include<bits/stdc++.h>
#define MAXN 105
using namespace std;
int a[MAXN];
int vis[MAXN];
int c[MAXN];
int main()
{
int n,t;
scanf("%d",&t);
while(t--)
{
memset(c,0,sizeof(c));
memset(vis,0,sizeof(vis));
scanf("%d",&n);
for(int i=1;i<=n;i++)
scanf("%d",&a[i]),vis[a[i]]=i;
for(int i=1;i<=n;i++)
{
int sign=1;
while(sign)
{
int x=vis[i];//小的位置
int y=vis[i]-1;
if(a[x]<a[y]&&y>0&&c[y]==0){
swap(vis[a[x]],vis[a[y]]);
swap(a[x],a[y]);
c[y]=1;
}
else{
sign=0;
}
}
}
for(int i=1;i<=n;i++)
printf("%d ",a[i]);
printf("\n");
}
return 0;
}
C. Platforms Jumping
题意:
一条长为n的河,你在0位置,要到n+1位置,河上有一些木板
木板的顺序不能改变,你可以改变木板的位置
你在x位置,你可以一次移动到(x+1,x+d),问你能否从0到n+1
解析:
如果木板的长度和>n,呢么就必然失败
贪心,尽可能将木板往右放,后面的木板可能连在一次
ac:
#include<bits/stdc++.h>
#define MAXN 2005
#define ll long long
using namespace std;
int a[MAXN];
int sum[MAXN];
int c[MAXN];
int main()
{
int n,m,d;
scanf("%d%d%d",&n,&m,&d);
for(int i=1;i<=m;i++)
scanf("%d",&a[i]);
for(int i=m;i>=1;i--)
sum[i]=sum[i+1]+a[i];
int sign=0,st=0;
for(int i=1;i<=m;i++)
{
int cc=n-(st+sum[i]);//在将(i,n)木板全部在一起不超过n的情况下,最远跳过的水域距离
cc=min(cc,d-1);
st=st+1+cc;
for(int j=st;j<=st+a[i]-1;j++)
c[j]=i;
st=st+a[i]-1;
}
if(st+d<=n||st>n)
{
printf("NO\n");
return 0;
}
printf("YES\n");
for(int i=1;i<=n;i++)
printf("%d ",c[i]);
printf("\n");
return 0;
}
https://codeforces.com/contest/1256/problem/D
题意:
给定一个二进制字符串,你右k次机会,每次可以选择两个相邻字符交换他们,问k次后最后字典序最小的情况
解析:
直接贪心
#include<bits/stdc++.h>
#define MAXN 1000005
#define ll long long
using namespace std;
char s[MAXN];
int main()
{
ll t,n,k;
scanf("%lld",&t);
while(t--)
{
scanf("%lld%lld",&n,&k);
scanf("%s",s+1);
ll g=1,sign=0,vis=0;
for(ll i=1;i<=n&&k;i++)
{
if(s[i]=='0'){
int d=min(k,i-g);
k=k-d;
sign=g+(i-g)-d;
if(d==i-g)
g++;
}
vis=i;
}
//g为直接0的情况,sign为最后移动移动的0的位置,vis为最后变换的位置
for(int i=1;i<=n;i++)
{
if(i<g||i==sign){
printf("0");
}
else if(i<=vis){
printf("1");
}
else{
printf("%c",s[i]);
}
}
printf("\n");
}
return 0;
}
E.题意:
给定一个数组,将数组分未若干份,每份至少有3个元素,每组的权值未其中极大-极小
怎么分使得所以组的权值和最小?输出分组
解析:
如果元素>=6,呢么显然将他们分成至少2组.
递推式:i-j>=0&&dp[i]>dp[i-j]+ee[i].v-ee[i-j+1].v
更新的时候,顺便记录路径
ac:
#include<bits/stdc++.h>
#define MAXN 200005
#define ll long long
using namespace std;
struct node
{
int v,id;
friend bool operator <(node a,node b)
{
return a.v<b.v;
}
}ee[MAXN];
int dp[MAXN];
int pre[MAXN];
int ans[MAXN];
int main()
{
int n;
scanf("%d",&n);
for(int i=1;i<=n;i++)
scanf("%d",&ee[i].v),ee[i].id=i;
sort(ee+1,ee+n+1);
memset(dp,0x3f3f3f,sizeof(dp));
dp[0]=0;
pre[0]=0;
for(int i=3;i<=n;i++)
{
for(int j=3;j<=5;j++)
{
if(i-j>=0&&dp[i]>dp[i-j]+ee[i].v-ee[i-j+1].v){
dp[i]=dp[i-j]+ee[i].v-ee[i-j+1].v;
pre[i]=i-j;
}
}
}
int tot=0;
for(int i=n;i>=1;i=pre[i])
{
++tot;
for(int j=i;j>pre[i];j--)
ans[ee[j].id]=tot;
}
printf("%d %d\n",dp[n],tot);
for(int i=1;i<n;i++)
printf("%d ",ans[i]);
printf("%d\n",ans[n]);
return 0;
}
https://codeforces.com/contest/1256/problem/F
题意:
给定两个字符串s和t,你可以选择一段长度为len的s的子串反转,将长度为len的t的一种子串反转
如果最后s和t能相同,输出YES
解析:
因为交换次数无限制,所以对于len>2的情况,都可能通过若干次len=2的操作实现,我们仅考虑len=2的情况
1.如果字符串字母构成不能,必定失败
2.如果字符串中有一个字符个数>1,呢么将两个相同的移动到两端之一,然后一定可以通过若干次操作将s和t相同
3.如果字符串中没有字符个数>1,这种字符串最多长为26,然后我们可能通过冒泡排序,将他们按字典序方式排序
如果排序次数相同,则YES,通过计算逆序数的方法原理是相同的
ac:
#include<bits/stdc++.h>
#define MAXN 200005
using namespace std;
char s[MAXN];
char c[MAXN];
int aa[30],bb[30];
int main()
{
int t,n;
scanf("%d",&t);
while(t--)
{
memset(aa,0,sizeof(aa));
memset(bb,0,sizeof(bb));
scanf("%d",&n);
scanf("%s",s+1);
scanf("%s",c+1);
for(int i=1;i<=n;i++){
aa[s[i]-'a']++;
bb[c[i]-'a']++;
}
int sign=0,maxs=0;
for(int i=0;i<26;i++){
if(aa[i]!=bb[i]){
sign=1;
}
maxs=max(maxs,aa[i]);
}
if(sign==1){//构成不同
printf("NO\n");
}
else{
if(maxs>1){//有重复字母
printf("YES\n");
}
else{
int va=0,vb=0;
for(int i=1;i<=n;i++)
{
for(int j=i+1;j<=n;j++)
{
if(s[j]>s[i]){
va++;
swap(s[j],s[i]);
}
if(c[j]>c[i]){
vb++;
swap(c[j],c[i]);
}
}
}
if(va%2==vb%2){//直接计算逆序数,判断逆序数相同
printf("YES\n");
}
else
printf("NO\n");
}
}
}
return 0;
}