Codeforces Round #598 (Div. 3) codeforces 1256

题目链接

昨天晚上的cf,和程磊开黑打的,听说div3好上分,本来还指望这场上130分直接把颜色改了,没想到翻车了......就过了俩题,A和D过了,B题是个贪心,没写好。

A水题。

B:

题意:给你一个n的全排列,你可以操作n-1次(或者少于n-1次),问操作完后使字典序最小,输出这个字典序最小的排列。对于每种操作,你可以选一个数i(1<i<=n-1),然后交换a[i]和a[i+1],并且i不能重复选。

思路:每次把要操作的区间的最小值找出来,然后把他弄到区间的最前面,然后区间往右推移,直到结束。

这种题,我总是思路不太清晰,边码边写,弄得很乱,调不出来。还是应该先把思路弄清再写!

#include<bits/stdc++.h>
#define mem(a,b) memset((a),b,sizeof(a))
#define de cout<<endl<<endl<<endl
typedef long long ll;
const int inf=0x3f3f3f3f;
const int maxn=1e5+10;
using namespace std;
int n,a[110],b[110],ans[110],p;
void print(int ans[])
{
    printf("%d",ans[1]);
    for(int i=2;i<=n;i++)
        printf(" %d",ans[i]);
    puts("");return ;
}
void solve()
{
    int minn=n+1,q;
    for(int i=p;i<=n;i++)
    {
        if(minn>a[i])
        {
            minn=a[i];
            q=i;
        }
    }
    if(minn==ans[p])
        {p++;return ;}
    for(int i=q-1;i>=p;i--)
        ans[i+1]=a[i];
    ans[p]=minn;
    p=q;
    for(int i=1;i<=q;i++)
        a[i]=ans[i];
    return ;
}
int main()
{
    int T;scanf("%d",&T);
    while(T--)
    {
        scanf("%d",&n);
        for(int i=1;i<=n;i++)
            scanf("%d",&a[i]),b[a[i]]=i;
        p=-1;
        for(int i=1;i<=n;i++)
            if(b[i]!=i)
                {p=i;break;}
        mem(ans,0);
        for(int i=1;i<=p-1;i++)
            ans[i]=a[i];
        if(p==-1)
            {print(a);continue;}
        while(1)
        {
            if(p>n)    break;
            solve();
        }
        print(ans);
    }
    return 0;
}

C:

题意:一个人位于0号位置,现在要跳到n+1号位置,每次跳跃能从x跳跃到x+1~x+d。给出m块木板的长度,现在要你安排这m块木板的位置(不改变相对顺序,并且不能重叠),输出最后的方案,或者这个人不能成功到n+1点。

思路:先判断用这m个板最多能走多远,如果这个最远路程还比n小,那就输出NO,否则就是YES。然后算出最远能走的路程和n的差值,这个差值就是你可以“浪费”的步数,然后尽可能早的把这些步数浪费掉,然后下面的按照最远的走法走就行了。(最远的走法既是每块隔板间隔d-1的距离)

#include<bits/stdc++.h>
#define mem(a,b) memset((a),b,sizeof(a))
#define de cout<<endl<<endl<<endl
typedef long long ll;
const int inf=0x3f3f3f3f;
const int maxn=1e5+10;
using namespace std;
int n,m,d,a[1010],ans[1010],sum[1010],cnt;
void print()
{
    int f=0,q=1,qq=0;
    for(int i=1;i<=cnt;i++)
    {
        for(int j=1;j<=ans[i];j++)
        {
            if(f==0)
                f++,printf("0"),qq++;
            else
                printf(" 0"),qq++;
        }
        for(int j=1;j<=a[q];j++)
        {
            if(f==0)
                f++,printf("%d",q),qq++;
            else
                printf(" %d",q),qq++;
        }
        q++;
    }
    for(int i=q;i<=m;i++)
    {
        for(int j=1;j<=a[i];j++)
        {
            if(f==0)
                f++,printf("%d",i),qq++;
            else
                printf(" %d",i),qq++;
        }
    }
    for(int i=qq+1;i<=n;i++)
    {
        if(f==0)
            f++,printf("0");
        else
            printf(" 0");
    }
    puts("");
}
int main()
{
    scanf("%d%d%d",&n,&m,&d);
    sum[0]=0;
    for(int i=1;i<=m;i++)
        scanf("%d",&a[i]),sum[i]=sum[i-1]+a[i];
    int S=sum[m]+(d-1)*(m+1);
    if(S<n)
        {printf("NO\n");return 0;}
    puts("YES");
    mem(ans,0);
    cnt=0;
    int p=n-sum[m];
    while(p>0)
    {
        if(p>=d-1)
            {p-=d-1,ans[++cnt]=d-1;continue;}
        ans[++cnt]=p;
        p=0;
    }
    print();
    return 0;
}

D:

题意:给出一个0101串,现在执行一次操作为:交换两个相连位置的字符。最多执行k次操作,输出最终得到的字典序最小串。这个题可以重复操作相同位置,这一点和B不一样。

思路:实际上这个还是比较好想的。

#include<bits/stdc++.h>
#define mem(a,b) memset((a),b,sizeof(a))
#define de cout<<endl<<endl<<endl
typedef long long ll;
const int inf=0x3f3f3f3f;
using namespace std;
const int maxn=1e6+10;
ll n,k;
char s[maxn];
int a[maxn],b[maxn],ans[maxn];
int main()
{
    int T;scanf("%d",&T);
    while(T--)
    {
        scanf("%I64d%I64d",&n,&k);
        scanf("%s",s+1);
        for(int i=1;i<=n;i++)
            a[i]=s[i]-'0';
        int cnt=0;
        for(int i=1;i<=n;i++)
            if(a[i]==0)
                b[++cnt]=i;
        if(cnt==0) {printf("%s\n",s+1);continue;}
        ll sum=0,num=0;
        int q=-1;
        while(1)
        {
            if(num==cnt)
                break;
            if(sum+b[num+1]-num-1<=k)
                {sum+=b[num+1]-num-1;num++;continue;}
            else
                {q=k-sum;break;}
        }
        for(int i=1;i<=num;i++)
            ans[i]=0;
        for(int i=1;i<=num;i++)
            a[b[i]]=1;
        int f=0;
        for(int i=num+1;i<=n;i++)
        {
            if(a[i]==0)
                f++;
            if(f==0)
                ans[i]=1;
            else
                ans[i]=a[i];
        }
        if(q!=-1)
        {
            ans[b[num+1]]=1;
            ans[b[num+1]-q]=0;
        }
        for(int i=1;i<=n;i++)
            cout<<ans[i];
        puts("");
    }
    return 0;
}

E:

题意:这个学校里面有n个学生,你需要给他们分成若干的队伍,每个队伍最少3个人。每个队伍定义差异值是这个队伍最强的人和最弱的人的能力值差。现在你需要构建若干个队伍,使得差异值的总和最小。

思路:首先排序。然后每个队伍一定人数最多为5个人,因为6个人就可以拆成两队,然后两队的代价一定是比一个队伍的代价小。这一点想到就非常简单了,dp就行了,然后回溯找分配方案。

#include<bits/stdc++.h>
#define mem(a,b) memset((a),b,sizeof(a))
#define de cout<<endl<<endl<<endl
typedef long long ll;
const ll inf=1e17;
const int maxn=2e5+10;
using namespace std;
int n,ans;
struct node{
    ll v;
    int id,idd;
}a[maxn];
ll dp[maxn];
bool cmp1(node a1,node a2)  {return a1.v<a2.v;}
bool cmp2(node a1,node a2)  {return a1.id<a2.id;}
int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
        scanf("%I64d",&a[i].v),a[i].id=i;
    sort(a+1,a+n+1,cmp1);
    if(n<=5)
    {
        printf("%d 1\n",a[n].v-a[1].v);
        printf("1");
        for(int i=2;i<=n;i++)   printf(" 1");
        puts("");return 0;
    }
    dp[1]=inf;dp[2]=inf;dp[3]=a[3].v-a[1].v;dp[4]=a[4].v-a[1].v;dp[5]=a[5].v-a[1].v;
    for(int i=6;i<=n;i++)
    {
        if(i>=8)
            dp[i]=min(min(dp[i-3]+a[i].v-a[i-2].v,dp[i-4]+a[i].v-a[i-3].v),dp[i-5]+a[i].v-a[i-4].v);
        else if(i>=7)
            dp[i]=min(dp[i-3]+a[i].v-a[i-2].v,dp[i-4]+a[i].v-a[i-3].v);
        else
            dp[i]=dp[i-3]+a[i].v-a[i-2].v;
    }
    printf("%I64d ",dp[n]);
    ans=0;
    for(int i=n;i>=3;i--)
    {
        if(i>=5&&dp[i]==dp[i-5]+a[i].v-a[i-4].v)
            a[i].idd=++ans,a[i-1].idd=ans,a[i-2].idd=ans,a[i-3].idd=ans,a[i-4].idd=ans,i-=4;
        else if(i>=4&&dp[i]==dp[i-4]+a[i].v-a[i-3].v)
            a[i].idd=++ans,a[i-1].idd=ans,a[i-2].idd=ans,a[i-3].idd=ans,i-=3;
        else if(i>=3&&dp[i]==dp[i-3]+a[i].v-a[i-2].v)
            a[i].idd=++ans,a[i-1].idd=ans,a[i-2].idd=ans,i-=2;
    }
    printf("%d\n",ans);
    sort(a+1,a+n+1,cmp2);
    printf("%d",a[1].idd);
    for(int i=2;i<=n;i++)
        printf(" %d",a[i].idd);
    puts("");
    return 0;
}

F:

题意:现在给你两个字符串,你可以进行任意次操作。每次操作需要在每个字符串都选择出长度为len的一个区间,然后将这个区间的字符都进行翻转。问你进行若干次操作后,这俩字符串能变成一样的吗?

思路:看了大佬的博客,感觉很妙。

按照这个顺序进行判断:

  1. 如果两个字符串存在不同的字符,那么肯定是NO
  2. 如果某个字符串存在两个相同的字符,那么一定是YES,因为可以就在这两个字符中进行无限次的翻转
  3. 如果两个字符串的逆的奇偶性相同,那么一定是YES

第三个怎么理解呢?在判断1和2之后,我们得到的一定是一个排列,问题就变成你可以翻转若干次,两个排列能否相同。

我们考虑我们同时翻转相同长度的,我们排列的逆一定会发生奇偶性的变化,那么如果一开始奇偶性就不同,那么不管怎么翻转,都不会相同。

推荐参考博客:https://www.cnblogs.com/qscqesze/p/11799050.html

#include<bits/stdc++.h>
#define mem(a,b) memset((a),b,sizeof(a))
#define de cout<<endl<<endl<<endl
typedef long long ll;
const ll inf=1e17;
const int maxn=2e5+10;
using namespace std;
int n,a[maxn],b[maxn],cnt1[110],cnt2[110];
char s1[maxn],s2[maxn];
int main()
{
    int T;scanf("%d",&T);
    while(T--)
    {
        scanf("%d",&n);
        scanf("%s%s",s1+1,s2+1);
        for(int i=1;i<=n;i++)
            a[i]=s1[i]-'a'+1;
        for(int i=1;i<=n;i++)
            b[i]=s2[i]-'a'+1;
        mem(cnt1,0);mem(cnt2,0);
        for(int i=1;i<=n;i++)
            cnt1[a[i]]++,cnt2[b[i]]++;
        int f=0,ff=0;
        for(int i=1;i<=26;i++)
            if(cnt1[i]!=cnt2[i])
                {f=1;break;}
        if(f)  {printf("NO\n");continue;}
        for(int i=1;i<=26;i++)
            if(cnt1[i]>1||cnt2[i]>1)
                {ff++;break;}
        if(ff)  {printf("YES\n");continue;}
        ll sum1=0,sum2=0;
        for(int i=1;i<=n;i++)
            for(int j=i+1;j<=n;j++)
                if(a[i]>a[j])
                    sum1++;
        for(int i=1;i<=n;i++)
            for(int j=i+1;j<=n;j++)
                if(b[i]>b[j])
                    sum2++;
        if((sum1%2)==(sum2%2))
            printf("YES\n");
        else
            puts("NO");
    }
    return 0;
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值