BestCoder Round #75 总结

116 篇文章 0 订阅
16 篇文章 0 订阅

King's Cake

 
 Accepts: 960
 
 Submissions: 1572
 Time Limit: 2000/1000 MS (Java/Others)
 
 Memory Limit: 65536/65536 K (Java/Others)
问题描述
阅兵式前一天,是国王的生日,大臣们为他准备了一个 n \times m(1\le n, m \le 10000)n×m(1n,m10000) 的蛋糕。他准备切蛋糕,但他切蛋糕有奇奇怪怪的癖好,他每次只切一刀,切下一个正方形蛋糕。请问它最多能切出多少个正方形蛋糕?
输入描述
第一行一个整数表示测试组数:T(0 < T\le1000)T(0<T1000) 。

每组数据占一行,每行两个整数 n \times m(1\le n, m \le 10000)n×m(1n,m10000),表示蛋糕的大小。
输出描述
TT 行,每行一个整数表示最多能切出的正方形蛋糕数量。
输入样例
2
2 3
2 5
输出样例
3
4
Hint
对于第一组数据,可切出一个 2\times22×2, 两个 1\times 11×1,共 33 个。

对于第一组数据,可切出两个 2\times22×2, 两个 1\times 11×1,共 4个。
来自官方的题解:

显然这很像求最大公约数的过程嘛,看这张神图:

http://blog.sengxian.com/algorithms/_image/gcd-extgcd/Euclidean_algorithm_1071_462.gif

所以每次 \gcdgcd 的时候累加答案即可,复杂度 O(\log\max(n, m)T)O(logmax(n,m)T)

当然你要是循环减应该也放过了。

#include<stdio.h>
#include<string.h>
#include<algorithm>
using namespace std;
#define LL __int64
const int maxm=1e5+10;
int main()
{
    int t;
    scanf("%d",&t);
    while(t--)
    {
        int n,m;
        scanf("%d%d",&n,&m);
        if(m<n)
            swap(m,n);
        int sum=0;
        while(m&&n)
        {
            if(m==1||n==1)
            {
                if(m==1)
                    sum+=n;
                else
                    sum+=m;
                break;
            }
            sum+=m/n;
            m=m%n;
            if(m<n)
                swap(m,n);
        }
        printf("%d\n",sum);
    }
    return 0;
}


4

King's Phone

 
 Accepts: 310
 
 Submissions: 2980
 Time Limit: 2000/1000 MS (Java/Others)
 
 Memory Limit: 65536/65536 K (Java/Others)
问题描述
阅兵式上,国王见到了很多新奇东西,包括一台安卓手机。他很快对手机的图形解锁产生了兴趣。

解锁界面是一个 3 \times 33×3 的正方形点阵,第一行的三个点标号 1, 2, 31,2,3,第二行的三个点标号 4, 5, 64,5,6,第三行的三个点标号 7, 8, 97,8,9。密码本身是一段序列,表示经过点的先后顺序,但遵循如下规则:

1. 密码至少经过四个点。

2. 不能重复经过同一个点。

3. 路径上的中间点不能跳过,除非已经被经过(34273427 是合法的,但 37243724 不合法)。

他想设置的密码的长度为正整数 k(1\le k\le 9)k(1k9),密码序列为 s_1 s_2...s_k(0\le s_i < INT\_MAX)s1s2...sk(0si<INT_MAX),他想知道这个密码序列是否合法,这个问题交给了你。
输入描述
第一行一个整数表示测试组数:T(0 < T\le100000)T(0<T100000) 。

每组数据占一行,每行第一个数 kk,设置密码的长度;接着 kk 个正整数,之间用空格隔开,表示密码序列 s_1s_2...s_ks1s2...sk
输出描述
TT 行。对每组数据,若合法输出 `valid`,否则输出 `invalid`。
输入样例
3
4 1 3 6 2
4 6 2 1 3
4 8 1 6 7
输出样例
invalid
valid
valid
Hint
对于第一组数据,1133 跳过了路径上的点 22,所以不合法。

对于第二组数据,1133 时点 22 已经被经过了,所以合法。

对于第三组数据,8\rightarrow 1 \rightarrow 6 \rightarrow 78167 路径均没有中间点,所以合法。
来自官方题解:

一个简单的模拟题,首先判断序列长度是否合法,接着判断 s_isi 是否在 [1, 9][1,9],最后扫一遍看看有没有重复以及跨过中间点的情况即可。

复杂度:O(nT)O(nT)

#include<stdio.h>
#include<string.h>
#include<algorithm>
using namespace std;
#define LL __int64
const int maxm=20;
int a[maxm];
int vis[maxm];
int main()
{
    int t;
    scanf("%d",&t);
    while(t--)
    {
        int n;
        scanf("%d",&n);
        memset(vis,0,sizeof(vis));
        int ok=0;
        for(int i=0; i<n; i++)
        {
            scanf("%d",&a[i]);
            if(a[i]>9||a[i]<=0)
                ok=1;
        }
        if(n<4||n>9)
            ok=1;
        if(ok)
            printf("invalid\n");
        else
        {
            vis[a[0]]=1;
            for(int i=1; i<n; i++)
            {
                if(vis[a[i]]==1)
                {
                    ok=1;
                    break;
                }
                else
                {
                    vis[a[i]]=1;
                    if(a[i]==1&&((a[i-1]==3&&vis[2]==0)||(a[i-1]==7&&vis[4]==0)||(a[i-1]==9&&vis[5]==0)))
                    {
                        ok=1;
                        break;
                    }
                    else if(a[i]==2&&a[i-1]==8&&vis[5]==0)
                    {
                        ok=1;
                        break;
                    }
                    else if(a[i]==3&&((a[i-1]==1&&vis[2]==0)||(a[i-1]==7&&vis[5]==0)||(a[i-1]==9&&vis[6]==0)))
                    {
                        ok=1;
                        break;
                    }
                    else if(a[i]==4&&a[i-1]==6&&vis[5]==0)
                    {
                        ok=1;
                        break;
                    }
                    else if(a[i]==6&&a[i-1]==4&&vis[5]==0)
                    {
                        ok=1;
                        break;
                    }
                    else if(a[i]==7&&((a[i-1]==3&&vis[5]==0)||(a[i-1]==1&&vis[4]==0)||(a[i-1]==9&&vis[8]==0)))
                    {
                        ok=1;
                        break;
                    }
                    else if(a[i]==8&&a[i-1]==2&&vis[5]==0)
                    {
                        ok=1;
                        break;
                    }
                    else if(a[i]==9&&((a[i-1]==7&&vis[8]==0)||(a[i-1]==3&&vis[6]==0)||(a[i-1]==1&&vis[5]==0)))
                    {
                        ok=1;
                        break;
                    }
                }
            }
            if(ok)
                printf("invalid\n");
            else
                printf("valid\n");
        }
    }
    return 0;
}

King's Order

 
 Accepts: 381
 
 Submissions: 1361
 Time Limit: 2000/1000 MS (Java/Others)
 
 Memory Limit: 65536/65536 K (Java/Others)
问题描述
国王演讲后士气大增,但此时战争还没有结束,国王时不时要下发命令。

由于国王的口吃并没有治愈,所以传令中可能出现:“让第三军-军-军,到前线去” 这样的命令。由于大洋国在军队中安插了间谍 , 战事紧急,很多时候前线的指挥官不能分清哪些命令真正来自国王。但国王的命令有一个特点,他每次连续重复的字符最多 33 次. 所以说他的命令中没有:“让第三军-军-军-军 , 到前线去”,但是可以有 :“让第三军-军 , 到前线去” 。

此时将军找到了你,你需要告诉他,给定命令的长度长度为 nn,有多少种不同的命令可以是国王发出的 。(也就是求长度为 nn 的合格字符串的个数)当然,国王可能说出一句话没有犯任何口吃,就像他那次演讲一样。

为了简化答案,国王的命令中只含有小写英文字母,且对答案输出模 10000000071000000007。

我们认为两个命令如果完全相同那么这两个字符串逐个比较就完全相同。
输入描述
第一行一个整数表示测试组数:T(T \le10)T(T10)。

每组数据占一行,每行一个正整数 n(n \le 2000)n(n2000) 表示字符串的长度。
输出描述
TT 行,每行一个整数表示合法的命令数量。
输入样例
2
2
4
输出样例
676
456950
Hint
两个中没有不符合要求的,所以答案为 26\times 26 = 67626×26=676

四个不符合要求的只有 `aaaa` `bbbb` ... `zzzz`总共 26 个

那么答案就是: 26^4-26 = 45695026426=456950
来自官方题解:

数一个长度为 nn 的序列 , 并且序列中不能出现长度大于 33 的连续的相同的字符 , 这玩意就是一个数位DP嘛。 定义 d[i][j][k]d[i][j][k] 为处理完 ii 个字符 , 结尾字符为 'a'+ja+j , 结尾部分已重复出现了 kk 次的方案数。 刷表转移一下就好啦。

复杂度:O(26 * 26 * nT)O(2626nT)


#include<stdio.h>
#include<string.h>
#include<algorithm>
using namespace std;
#define LL __int64
const int maxm=2e3+10;
const LL MOD = 1e9+7;
LL dp[maxm][300][5];
LL sum(LL a,LL b)
{
    return (a+b)%MOD;
}
void M()
{
    memset(dp,0,sizeof(dp));
    for(int i=1; i<=26; i++)
    {
        dp[1][i][1] = 1;
    }
    for(int i=2; i<=2000; i++)
    {
        for(int j=1; j<=26; j++)
        {
            for(int k=1; k<=26; k++)
            {
                if(j!=k)
                {
                    for(int q=1; q<=3; q++)
                    {
                        dp[i][j][1] =  sum(dp[i][j][1],dp[i-1][k][q]);
                    }
                }
                else
                {
                    dp[i][j][2] = sum(dp[i][j][2],dp[i-1][k][1]);
                    dp[i][j][3] = sum(dp[i][j][3],dp[i-1][k][2]);
                }
            }
        }
    }
}
int main()
{
    int T;
    M();
    scanf("%d",&T);
    while(T--)
    {
        int n;
        scanf("%d",&n);
        LL ans = 0;
        for(int i=1; i<=26; i++)
        {
            for(int j=1; j<=3; j++)
            {
                ans = sum(ans,dp[n][i][j]);
            }
        }
        printf("%I64d\n",ans);
    }
    return 0;
}


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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值