SDKD 2019 Winter Training Series C2 2nd Round题解

 

SDKD 2019 Winter Training Series C2 2nd Round题解

 

目录

A题You Are Given Two Binary Strings 1

B题You Are Given a Decimal String... 2

C You Are Given a WASD-string... 4

D Print a 1337-string... 6

E You Are Given Some Strings... 8

F题You Are Given Some Letters... 11

 

A题You Are Given Two Binary Strings

难度:签到

知识点分类:思维

题意:给出两个二进制数x和y,给出一个公式s = x+y*2^k,s也是二进制数。求出k使s反转后的字典序最小。

思路:如果要使s反转后的字典序尽量小,那就应该让s的低位有尽量多的0.而如果要使0尽量多,有两种方式,一是1+1,二是0+0.而公式中的y*2^k相当于往y的低位中添加k个0.先用string模拟二进制加法打表发现决定字典序大小的是最低的1.所以做法为先从低位到高位找到y中的第一个1的位置,然后在x中从相同的位置开始,从低位到高位找到第一个1的位置,这两个位置之差就是答案。

代码:

#include<bits/stdc++.h>

using namespace std;

int main()

{

    int t;

    cin>>t;

    string x, y;

    while(t--)

    {

        cin>>x;

        cin>>y;

        int len1 = x.size(), len2 = y.size();

        int dir = 0;

        for(int i = len2-1; i >= 0; i--)

        {

            int t = y[i]-'0';

            if(t == 1)

                break;

            else

                dir++;

        }

        int num = 0;

        for(int i = len1-dir-1; i >= 0; i--)

        {

            int t = x[i]-'0';

            if(t == 1)

                break;

            else

                num++;

        }

        cout<<num<<endl;

    }

    return 0;

}

String模拟二进制加法:https://paste.ubuntu.com/p/2DwMbWkXZ4/

 

B题You Are Given a Decimal String...

难度:中等

知识点分类:floyd或暴力

题意:给一个字符串,给定x和y时,问要进行多少次操作,可以使得字符串符合要求(每位上的数字是前一位上的数字加上x或y的和的个位数)。

 

思路分析:已知前后两个位置上的数,现在要加入数字,使得能依靠x和y建立联系,也就是知道起点和终点,也知道可想通路有哪些(只要和x,y有关),现在要找一条最短路径,可以用floyd。

估算可行性,由于数字只是0-9,floyd也是10^3=1e3。有10*10对x和y,但是(x,y)和(y,x)无区别,即(1e2/2)*1e3<1e5,可行。

 

代码:

#include<cstdio>

#include<bits/stdc++.h>

#define inf 0x3f3f3f3f

using namespace std;

const int maxn=2e6+10;

char s[maxn];

int solve(int a,int b)

{

    int len=strlen(s);

    int road[11][11];

    int ans=0;

        memset(road,inf,sizeof(road));

        for(int d=0;d<10;++d)

            {

                road[d][(d+a)%10]=1;

                road[d][(d+b)%10]=1;

            }

 

        for(int k=0;k<10;++k)

            for(int i=0;i<10;++i)

            for(int j=0;j<10;++j)

            {

                if(road[i][k]==inf||road[k][j]==inf)continue;

                road[i][j]=min(road[i][j],road[i][k]+road[k][j]);

            }

 

        for(int i=1;i<len;++i)

        {

        int p1=s[i-1]-'0',p2=s[i]-'0';

        if(road[p1][p2]>=inf)return -1;

        ans+=road[p1][p2]-1;

        }

    return ans;

}

int have[10][10];

int main(void)

{

    scanf("%s",s);

   for(int i=0;i<=9;++i)

    for(int j=0;j<=9;++j)

   {

      if(i<=j){have[i][j]=solve(i,j);}

      else have[i][j]=have[j][i];

 

      printf("%d%s",have[i][j],j==9?"\n":" ");

   }

   return 0;

}

```

 

C题 You Are Given a WASD-string...

难度:中等

知识点分类:思维

题意:一个机器人在根据上下左右的指令走路,现在问添加一个指令后,最小能用多大的矩形覆盖机器人所走的路径。

思路分析:

上下左右其实是二维,因此上下和左右独立,故可记上(左)为-1,下(右)为+1.

任加一个指令后,就是将后面走的路径朝某个方向平移一格,即对后面的位置影响是都+1或-1.比如

标号

0

1

2

3

4

5

6

7

8

位置值

0

1

2

1

0

-1

-2

-1

-2

我们插入一个数+1 会有3种情况:

A. 最大值+1,最小值也加1,即新加指令后不能减小覆盖矩形的面积。

如果在标号1和2之间插入+1,那么之后的位置就都会+1,由于最大值,最小值都在在插入位置之后,因此都增加1.

B. 最大值+1,最小值不变,这时新加的指令会增大覆盖矩形面积。

容易想到这种情形会发生在 最小值的位置之后,最大值的位置之前。

C.最大值不变,最小值+1, 矩形面积减小。

这种情形正是我们要找的,如果能找到在最大值之后,最小值之前的位置加1,那么就能减小矩阵面积。

即如果存在一个位置在最大值之后,最小值之前,+1能实现面积减小,同样,如果存在一个位置在最小值之后,最大值之前,-1能实现面积减小。

代码

#include<cstdio>

#include<bits/stdc++.h>

#define ll long long

using namespace std;

const int maxm=2e5+10;

char comd[maxm];

int flag;

int solve(char a,char b)

{

    int len=strlen(comd);

    int sum=0,min_fir=-1,min_las=-1,max_fir=-1,max_las=-1;

    int minn=0,maxn=0,c=0;

    for(int i=0;i<len;++i)

    {

 

        if(comd[i]==a)sum++;

        else if(comd[i]==b)sum--;

        else continue;

        if(sum==minn)min_las=i;

        if(sum<minn){min_fir=min_las=i;minn=sum;}

        if(sum==maxn)max_las=i;

        if(sum>maxn){max_fir=max_las=i;maxn=sum;}

        c++;

    }

    ll ans=maxn-minn+1;

    if(c>1&&(min_fir>max_las||min_las<max_fir))

    {

        flag++;

        return --ans;

    }

    return ans;

}

int main(void)

{

    int cas;

    scanf("%d",&cas);

    while(cas--)

    {

        scanf("%s",comd);

        flag=0;

 

        ll dx=solve('D','A');

        ll sx=solve('S','W');

        ll ans;

        if(flag==2)ans=min(dx*(sx+1),(dx+1)*sx);

        else ans=dx*sx;

        printf("%lld\n",ans);

    }

    return 0;

}

 

D题 Print a 1337-string...

难度:简单

知识点分类:构造

题意:给定n,构建一个字符串,使得其找得到n个子串“1337”。

思路分析:

如果 C^a_2=n,那么直接写为 1 3 ....3(a个3) 7。

否则, 构建为 1 3 3 7...7(a个7) 3 ... 3(b个3)7,

这个序列中1337个数为 (a+1)+2*b+C^b_2,寻找合适的a,b使式子等于 n即可。a+1表示从第一个连续3取2个3,2*b表示从第一个和第2个连续3中各取1个,

C^b_2表示第2个连续3中取2 个。

代码:

#include<cstdio>

#include<bits/stdc++.h>

using namespace std;

const int big=1e9+10;

const int maxn=1e5+10;

int all[maxn],n;

void biao()

{

    int i;

    for( i=1;;++i)

    {

        int a=i*(i-1)/2;

        all[i]=a;

        if(a>big)break;

    }

}

int main(void)

{

    int cas;

    biao();

    scanf("%d",&cas);

    while(cas--)

    {

        scanf("%d",&n);

        int i=1;

        while(all[i]<=n){i++;}

        if(all[i-1]==n)

        {

            int a=i-1;

            printf("1");

            while(a--)printf("3");

            printf("7\n");

        }

        else

        {

            int b=i,a;

            while(1){

                     a=n-all[b]-b*2-1;

                    if(a>0)break;

                    b--;

            }

            printf("133");

            while(a--)printf("7");

            while(b--)printf("3");

            printf("7\n");

        }

}

    return 0;

}

 

E题 You Are Given Some Strings... 

难度:中等

知识点分类:ac自动机

题意:给一个模板字符串,给n个子字符串,任意2个子字符串组成一个字符串,问模板中出现过几个这样的合成字符串。

思路分析:一个模板,n个字符串,ac自动机可以实现匹配,但是n*n个太多了。

可以分成2个,前ac自动机的匹配和后ac自动机,比如位置i的字符,是前面匹配单词的最后一个字符,而i+1位置的字符,是后面匹配单词的第一个字符(要实现这种,那么在存fault链的时候要反向,模板也要反向。)

代码:

#include <bits/stdc++.h>

using namespace std;

typedef long long ll;

const int maxn = 2e5+25;

char s[maxn],ss[maxn];

int n;

struct AC_Automaton

{

    int tire[maxn][26],num[maxn*26],fail[maxn*26],dp[maxn],cnt = 0;

    void init(int x)

    {

        for(int i = 0; i < 26; ++i) tire[x][i] = 0;

        num[x] = 0;

    }

    void Build_ac(char *s)

    {

        int root = 0,len = strlen(s),t;

        for(int i = 0; i < len; ++i)

{            t = s[i] - 'a'

           if(!tire[root][t])

{tire[root][t] = ++cnt;

                init(cnt);

            }

            root = tire[root][t];

        }

        num[root]++;

    }

    void Get_fail()

    {

        queue<int> p;

        for(int i = 0; i < 26; ++i){

            int son = tire[0][i];

            if(son){

                fail[son] = 0;                p.push(son);

            }

            else tire[0][i] = 0;

        }

        while(!p.empty()){

            int x = p.front(); p.pop();

            num[x] += num[fail[x]];

            for(int i = 0; i < 26; ++i){

                if(tire[x][i]) {

                    int son = tire[x][i];

                    fail[son] = tire[fail[x]][i]; p.push(son);

                }

                else tire[x][i] = tire[fail[x]][i];

            }

        }

    }

    void Query_ac(char *s)

    {

        int root = 0,len = strlen(s);

        for(int i = 0; i < len; ++i){

            root = tire[root][s[i]-'a'];

            dp[i] = num[root];

        }

    }

}AC,ac;

int main()

{

    scanf("%s %d",s,&n);

    while(n--)

    {

        scanf("%s",ss);

        AC.Build_ac(ss);

        reverse(ss,ss+strlen(ss));

        ac.Build_ac(ss);

    }

    AC.Get_fail(); ac.Get_fail();

    AC.Query_ac(s);

    reverse(s,s+strlen(s));

    ac.Query_ac(s);

    ll ans = 0; int len = strlen(s);

    for(int i = 0;i < len; ++i) ans += (ll)AC.dp[i]*ac.dp[len-i-2];

    printf("%I64d",ans);

return 0;}

 

 

F题You Are Given Some Letters...

难度:困难

知识点分类:思维

题意:给出两个整数a和b,设字符串s仅由a个字符’A’和b个字符’B’组成,求出所有的字符串s以及他们的最小周期k,输出不同的k的个数。

思路:枚举k,然后判断。判断的方法为将s每k个分为一个块,判断有没有存在合法的方案,需要用到整除分块。需要满足每个块中字符’A’和字符’B’的数量相等,并且块要填满。设len为总长度,m为完整的块数,numa和numb分别为每个块中字符’A’和字符’B’的数量。有:

len = a+b;  m = floor(len/k);

numa+numb = k;   numa*m <= a;  numb*m <= b;

numa <= floor(a/m);    numb <= floor(b/m);

a-m*numa <= 1*numa;   b-m*numb <= 1*numb;

numa >= ceil(a/(m+1));  numb >= ceil(b/(m+1));

综上,有ceil(a/(m+1)) <= numa <= floor(a/m);ceil(b/(m+1)) <= numb <= floor(a/m);

代码:

#include <iostream>

#include <cstdio>

#include <cstring>

#include <algorithm>

#include <string>

#include <iomanip>

#include <sstream>

#include <cmath>

#define MAXN 100010

using namespace std;

 

int main()

{

    int a, b;

    scanf("%d%d",&a,&b);

    int len = a+b, ans = 0;

    int j;

    for(int i = 1; i <= len; i = j+1)//整除分块,如果直接i++,会有很多相同的m值。

    {                                //这样写一是降低时间复杂度,二是排除掉相同的m值

        int m = len/i;//完整的块数;  j = len/m;

        if(a < m|| b < m)

            continue;

        int shang1 = a/m, shang2 = b/m;

        int xia1 = ceil((double)a/(m+1)), xia2 = ceil((double)b/(m+1));

        if(xia1 <= shang1&& xia2 <= shang2)

        {

            ans += max(0, min(j, shang1+shang2)-max(i, xia1+xia2)+1);

        }

    }

    printf("%d\n",ans);

    return 0;

}

发布了145 篇原创文章 · 获赞 26 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/qq_43235540/article/details/104352667