codeforce 1202

A题-You Are Given Two Binary Strings
题意:给出两个二进制数x和y,给出一个公式s = x+y2^k,s也是二进制数。求出k使s反转后的字典序最小。
思路:如果要使s反转后的字典序尽量小,那就应该让s的低位有尽量多的0.而如果要使0尽量多,有两种方式,一是1+1,二是0+0.而公式中的y
2^k相当于往y的低位中添加k个0.先用string模拟二进制加法打表发现决定字典序大小的是最低的1.所以做法为先从低位到高位找到y中的第一个1的位置,然后在x中从相同的位置开始,从低位到高位找到第一个1的位置,这两个位置之差就是答案。
代码:https://paste.ubuntu.com/p/JM3kMYkzHJ/
String模拟二进制加法:https://paste.ubuntu.com/p/2DwMbWkXZ4/

B
题意:给一个字符串,给定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,可行。
解法:floyd,最短路。

#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… CodeForces - 1202C

参考博客:https://blog.csdn.net/weixin_43720526/article/details/100025698
题意:一个机器人在根据上下左右的指令走路,现在问添加一个指令后,最小能用多大的矩形覆盖机器人所走的路径。
思路分析
上下左右其实是二维,因此上下和左右独立,故可记上(左)为-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… CodeForces - 1202D
题意:给定n,构建一个字符串,使得其找得到n个子串“1337”。
思路分析
如果 C 2 a = n 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 2 b (a+1)+2*b+C^b_2 ,寻找合适的a,b使式子等于 n即可。a+1表示从第一个连续3取2个3,2*b表示从第一个和第2个连续3中各取1个,
C 2 b 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… CodeForces - 1202E
题意:给一个模板字符串,给n个子字符串,任意2个子字符串组成一个字符串,问模板中出现过几个这样的合成字符串。
思路分析:一个模板,n个字符串,ac自动机可以实现匹配,但是n*n个太多了。
可以分成2个,前ac自动机的匹配和后ac自动机,比如位置i的字符,是前面匹配单词的最后一个字符,而i+1位置的字符,是后面匹配单词的第一个字符(要实现这种,那么在存fault链的时候要反向,模板也要反向。)
类型:ac自动机

#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; numam <= a; numbm <= b;
numa <= floor(a/m); numb <= floor(b/m);
a-mnuma <= 1numa; b-mnumb <= 1numb;
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);
代码:https://paste.ubuntu.com/p/vBTHRNsdx5/

发布了114 篇原创文章 · 获赞 22 · 访问量 6993

猜你喜欢

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