Codeforces #301 Div 2 简要题解

比赛总结

这次比赛和上次发挥得差不多,做了四个题,比较不爽的是其中两个题因为没读清楚题都wa了样例,这里损失了点罚时
提交记录

在正式和非正式选手中排名144名(可以看出我的罚时有多么惨了吧233,每个题的得分基本上打了一半的折扣)
这里写图片描述
在正式选手里排名15名

A. Combination Lock

题目链接

http://codeforces.com/contest/540/problem/A

题目大意

给你一个大小为 n 的密码锁,每次转动(0->1,1->2…9->0,1->0…)需要一单位时间,给你密码锁初始状态和终止状态,问最少要操作多长时间才行。

思路

水题,不解释

代码

#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <algorithm>

using namespace std;

char s[1100];
int st[1100],ed[1100];

int main()
{
    int n,ans=0;
    scanf("%d",&n);
    scanf("%s",s+1);
    for(int i=1;i<=n;i++)
        st[i]=s[i]-'0';
    scanf("%s",s+1);
    for(int i=1;i<=n;i++)
        ed[i]=s[i]-'0';
    for(int i=1;i<=n;i++)
    {
        int t=abs(st[i]-ed[i]);
        t=min(t,st[i]+10-ed[i]);
        t=min(t,ed[i]+10-st[i]);
        ans+=t;
    }
    printf("%d\n",ans);
    return 0;
}

B. School Marks

题目链接

http://codeforces.com/contest/540/problem/B

题目大意

给你一个长度为 n 的序列,序列里要求每一项在区间 [1,p] ,其中前 k 项已经填好了,要你构造完剩余的 nk 项,使得所有项之和不超过 x ,且中位数大于等于 y ,输出一组可行解。

思路

我们对已经填好的前 k 项进行排序,问题转变为在这个有序序列里不断地加入 nk 项元素,让有序序列的 n+12 项大于等于 y 。如果我们想要让数字为 y 的最左边的项往右边走,就要加入 <y 的数字,如果我们想要让数字为 y 的最左边的项往左边走,就要加入 >=y 的数字,但是题目又限制所有项之和不超过 x ,因此我们要加入的项的数字都必须尽量小。因此我们加入的数字要么是 1 ,要么是 y 。我们尽量先填 y ,如果发现还没有填的部分全部填1的话就会使所有数字之和超出 x ,我们就换成填1,这样构造完的序列如果不能满足条件的话,那么显然就是无解了。

代码

#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <algorithm>
#include <map>

#define MAXN 1100

using namespace std;

int a[MAXN],tmp[MAXN];
int n,K,P,L,R,sum=0;

int main()
{
    scanf("%d%d%d%d%d",&n,&K,&P,&R,&L);
    for(int i=1;i<=K;i++) scanf("%d",&a[i]),tmp[i]=a[i],sum+=a[i];
    if(sum+n-K>R)
    {
        printf("-1\n");
        return 0;
    }
    for(int i=K+1;i<=n;i++)
    {
        if(sum+L+n-i<=R)
        {
            a[i]=L;
            sum+=L;
        }
        else
        {
            a[i]=1;
            sum++;
        }
        tmp[i]=a[i];
    }
    sort(tmp+1,tmp+n+1);
    if(tmp[(1+n)/2]>=L)
    {
        for(int i=K+1;i<=n;i++) printf("%d ",a[i]);
        printf("\n");
    }
    else printf("-1\n");
    return 0;
}

C. Ice Cave

题目链接

http://codeforces.com/contest/540/problem/C

题目大意

给你一个 n m 列的地图,每个格子上要么是’.’,要么是’X’,你只能经过格子’.’。你每经过一个格子,就会把它从’.’变成’X’,问你是否能这样从 (xs,ys) 走到 (xt,yt) ,并且走到 (xt,yt) 后,再绕一圈走回已经变成’X’的 (xt,yt)

思路

直接BFS就好了,如果碰见已经变成了’X’的 (xt,yt) 就表明存在这样的路线,否则如果当前的格子是’X’就不继续拓展,否则继续往四个方向分别拓展移动

为什么这样做就是对的呢?因为如果最先存在一条走到还是’.’的格子 (xt,yt) ,就可以继续走,并转一圈回到’X’。之后走到已经变为’X’的 (xt,yt) 的路线的话,肯定是围绕着最开始的那个路径的终点绕了一圈,正好就给 (xt,yt) 留了几个空格(最少三个),就能绕一圈走回来了

代码

#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <algorithm>
#include <map>

#define MAXN 610

using namespace std;

int n,m;
char mp[MAXN][MAXN];
int sx,sy,tx,ty;
pair<int,int> q[MAXN*MAXN*4];
int xx[]={1,-1,0,0},yy[]={0,0,1,-1};

bool inMap(int i,int j)
{
    if(i<1||i>n||j<1||j>m) return false;
    return true;
}

int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++) scanf("%s",mp[i]+1);
    scanf("%d%d",&sx,&sy);
    scanf("%d%d",&tx,&ty);
    int h=0,t=1;
    q[h]=make_pair(sx,sy);
    while(h<t)
    {
        pair<int,int>now=q[h++];
        int nowx=now.first,nowy=now.second;
        for(int dir=0;dir<4;dir++)
        {
            int nextx=nowx+xx[dir],nexty=nowy+yy[dir];
            if(!inMap(nextx,nexty)) continue;
            if(mp[nextx][nexty]=='X')
            {
                if(nextx==tx&&nexty==ty)
                {
                    printf("YES\n");
                    return 0;
                }
                continue;
            }
            mp[nextx][nexty]='X';
            q[t++]=make_pair(nextx,nexty);
        }
    }
    printf("NO\n");
    return 0;
}

D. Bad Luck Island

题目链接

http://codeforces.com/contest/540/problem/D

题目大意

现在有 r 个石头, s 个剪刀, p 个布,每回合会从中随机挑选两种种类的物品玩石头剪刀布,输的那方会减少一个物品,问最终只有石头存活、只有剪刀存活、只有布存活的期望各是多少。

思路

显然是个概率DP题。用 f[i][j][k] 表示还有 i 个石头, j 个剪刀, k 个布的情况的期望。 f[i][j][k] 是从 f[i+1][j][k] f[i][j+1][k] f[i][j][k+1] 推来的,现在考虑 f[i][j+1][k] 如何转移到 f[i][j][k] 。显然是上一局石头和剪刀碰面了,根据乘法计数,这种情况共有 i(j+1) 种,两种不同种类的物品碰面总共有 i(j+1)+(j+1)k+ik 种情况,因此出现石头和剪刀碰面的概率是 i(j+1)i(j+1)+(j+1)k+ik f[i][j][k]+=i(j+1)i(j+1)+(j+1)k+ikf[i][j+1][k] ,其他两种情况也差不多

代码

#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <algorithm>
#include <map>

#define MAXN 610

using namespace std;

double f[110][110][110];
int r,s,p;

double DFS(int i,int j,int k)
{
    if(f[i][j][k]) return f[i][j][k];
    //if(!i&&!j&&!k) continue;
    //if(i==r&&j==s&&k==p) continue;
    if(j+1<=s&&i) f[i][j][k]+=DFS(i,j+1,k)*(double)(i*(j+1))/(double)(i*(j+1)+(j+1)*k+k*i);
    if(k+1<=p&&j) f[i][j][k]+=DFS(i,j,k+1)*(double)(j*(k+1))/(double)(i*j+j*(k+1)+(k+1)*i);
    if(i+1<=r&&k) f[i][j][k]+=DFS(i+1,j,k)*(double)((i+1)*k)/(double)((i+1)*j+j*k+k*(i+1));
    return f[i][j][k];
}

int main()
{
    scanf("%d%d%d",&r,&s,&p);
    f[r][s][p]=1;
    double ans1=0,ans2=0,ans3=0;
    for(int i=1;i<=r;i++) ans1+=DFS(i,0,0);
    for(int i=1;i<=s;i++) ans2+=DFS(0,i,0);
    for(int i=1;i<=p;i++) ans3+=DFS(0,0,i);
    printf("%.11lf %.11lf %.11lf\n",ans1,ans2,ans3);
    return 0;
}

E. Infinite Inversions

题目链接

http://codeforces.com/contest/540/problem/E

题目大意

给你一个无限长的序列 1,2,3... ,以及 q 个操作,每次操作会交换这个序列里下标为 x y 的元素。问最终这个序列里有多少对逆序对

思路

假如每次操作的下标的范围是在 [1,n] 范围内,则显然这个序列只保留 [1,n] 部分的话答案是不变的。

所以我们可以用map来模拟交换元素的操作,然后对被修改过的那些序列元素做离散化,得到这些被修改的数字的排名、以及排名为 i 的数字是什么,然后我们可以离散化出一个新的序列,这个序列长度最多为 2q ,我们先求出这个序列的逆序对个数,这些逆序对 (ai,bi) aibi 都是被操作过的数字。然后我们还需要求出其他的逆序对 (ai,bi) aibi 里有一个是被操作过的数字。

看下面这个序列:
6 2 3 4 9 1 7 8 5
对于第一个数字6而言,它现在在位置1,原来在位置6, [1,6] 区间里,有3个被操作过的数字,还有3个是没有被操作过的数字,因此给最终答案贡献3

以此类推,我们枚举被修改的每个数字,找到它当前所在的下标和初始的下标之间的区间里有多少个没被操作过的数字,添加进答案

代码

#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <algorithm>
#include <map>

#define MAXN 410000
#define lowbit(x) ((x)&(-(x)))

using namespace std;

typedef long long int LL;

map<int,int>mp,mp2; //mp2[i]=数字i的排名
map<int,int>::iterator it,it2;
LL bit[MAXN];
int n;

void update(int x,LL val)
{
    while(x<=n)
    {
        bit[x]+=val;
        x+=lowbit(x);
    }
}

LL query(int x)
{
    LL ans=0;
    while(x)
    {
        ans+=bit[x];
        x-=lowbit(x);
    }
    return ans;
}

int a[MAXN]; //a[]里保存的是离散化后的序列
pair<int,int> opt[MAXN];
int sta[MAXN*2],top=0; //sta[i]=排名为i的数字

bool cmp(int x,int y)
{
    return sta[x]<sta[y];
}

LL ans=0;

int main()
{
    int q;
    scanf("%d",&q);
    for(int i=1;i<=q;i++)
    {
        int x,y;
        scanf("%d%d",&x,&y);
        it=mp.find(x);
        it2=mp.find(y);
        if(it==mp.end()) mp[x]=x;
        if(it2==mp.end()) mp[y]=y;
        opt[i]=make_pair(x,y);
        it=mp.find(x);
        it2=mp.find(y);
        swap(it->second,it2->second);
    }
    for(it=mp.begin();it!=mp.end();it++)
        mp2[it->second]=++top,sta[top]=it->second;
    /*for(it=mp2.begin();it!=mp2.end();it++) cout<<"num: "<<it->first<<"rank: "<<it->second<<endl;*/
    for(int i=1;i<=top;i++) a[i]=i;
    for(int i=1;i<=q;i++)
        swap(a[mp2[opt[i].first]],a[mp2[opt[i].second]]);
    n=top;
    for(int i=n;i>=1;i--)
    {
        ans+=query(a[i]-1);
        update(a[i],1);
    }
    for(it=mp.begin();it!=mp.end();it++)
        ans+=abs(it->second-it->first)-abs(mp2[it->second]-mp2[it->first]);
    printf("%I64d\n",ans);
    return 0;
}

你可能感兴趣的:(Codeforces #301 Div 2 简要题解)