BestCoder Round #74

http://bestcoder.hdu.edu.cn/contests/contest_show.php?cid=675

1001 - King’s Cake

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

http://bestcoder.hdu.edu.cn/images/solution/677-1.gif

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

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

#include<iostream>
#include<stdio.h>
using namespace std;
int T;
int n,m;
int ans=0;
void solve(int a,int b)
{
    if(a<b) swap(a,b);
    if(b==0) return ;
    ans+=a/b;
    a%=b;
    solve(a,b);
}
int main()
{
    scanf("%d",&T);
    while(T--)
    {
        ans=0;
        scanf("%d%d",&n,&m);
        solve(n,m);
        printf("%d\n",ans);    
    }

}

1002 - King‘s Phone

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

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

#include<iostream>
#include<stdio.h>
using namespace std;
int T;
int n;
int vis[10];
int a[10];
bool OK()
{
    for(int i=1;i<=n;i++)
    {
        int aa=a[i],bb=a[i+1];
        if(vis[aa]==1) return 0;
        if(i==n) return 1;
        vis[aa]=1;

            if(aa>bb) swap(aa,bb);
            if(bb==aa+1) continue;
            if(aa%3==1&&bb==aa+2) if(!vis[aa+1]) return 0;
            if(aa==1&&bb==9) if(!vis[5]) return 0;
            if(aa==3&&bb==7) if(!vis[5]) return 0;
            if(aa<=3&&bb==aa+6) if(!vis[aa+3]) return 0; 

    }
    return 1;
}
int main()
{
    scanf("%d",&T);
    while(T--)
    {
        int flag=0;
        scanf("%d",&n);
        for(int i=1;i<=10;i++) vis[i]=0;
        for(int i=1;i<=n;i++)
        {
            scanf("%d",&a[i]);
            if(a[i]<=0||a[i]>9) flag=1;
        }
        if(n>=10||n<4||flag==1)
        printf("invalid\n");
        else 
        {
            if(!OK())
            printf("invalid\n");
            else
            printf("valid\n");
        }
    }

}

1003 - King’s Order

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

复杂度:O(26∗26∗nT)O(26 * 26 * nT)O(26∗26∗nT)
1004 - King’s Game

约瑟夫问题的一个变种,然而题目全部是在唬人,就是一个简单的递推。虽然我知道有人会打表。。。

我们看看裸的约瑟夫是怎么玩的:nnn 个人,每隔 kkk 个删除。

由于我们只关心最后一个被删除的人,并不关心最后的过程,所以,我们没有必要也不能够模拟整个过程。我们用递推解决。假设有nnn个人围成环,标号为[0,n−1][0,n-1][0,n−1]从000开始的好处是取模方便),每数kkk个人杀一个的情况下,最后一个存活的人的编号是f[n]f[n]f[n]。

我们有f[1]=0f[1]=0f[1]=0,这不需要解释。

接着考虑一般情况f[n]f[n]f[n],第一个杀死的人的编号是k−1k-1k−1,杀死后只剩下n−1n-1n−1个人了,那么我们重新编号!

原来编号为k的现在是000号,也就是编号之间相差333我们只要知道现在n−1n-1n−1个人的情况最后是谁幸存也就知道nnn个人的情况是谁幸存。幸运的是f[n−1]f[n-1]f[n−1]已经算出来了那f[n]f[n]f[n]就是在f[n−1]f[n-1]f[n−1]的基础上加上一个kkk即可不要忘记总是要取模。

http://bestcoder.hdu.edu.cn/images/solution/677-2.png

所以递推式子是: f[i]={ 0 i=1 (f[i - 1] + k) mod i otherf[i] =

{ 0 \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ i=1  (f[i - 1] + k) mod i \ \ \ \ \ \ other
f[i]={​​​​​ 0 i=1​ (f[i - 1] + k) mod i other​​

此题只用在原版约瑟夫问题上加一维,由于依次隔 1,2,3…n−11, 2, 3…n - 11,2,3…n−1 个人删除,所以用 f[i][j]f[i][j]f[i][j] 表示 iii 个人,依次隔 j,j+1…j+i−1j, j + 1… j + i - 1j,j+1…j+i−1 个人的幸存者标号。

根据刚才的重标号法,第一次 j−1j - 1j−1 号出局,从 jjj 开始新的一轮,从 j+1j + 1j+1 开始清除,剩余 i−1i - 1i−1 个人,也有递推式子:

f[i][j]={ 0 i=1 (f[i - 1][j+1] + j) mod i otherf[i][j] =

{ 0 \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ i=1  (f[i - 1][j+1] + j) mod i \ \ \ \ \ \ other
f[i][j]={​​​​​ 0 i=1​ (f[i - 1][j+1] + j) mod i other​​

答案就是 f[n][1]+1f[n][1] + 1f[n][1]+1(将标号转移到 [1,n][1, n][1,n]),问题轻松解决。

复杂度:预处理 O(n2)O(n^2)O(n​2​​),查询 O(1)O(1)O(1),总复杂度 O(n2)O(n^2)O(n​2​​)。由于可以滚动数组以及常数太小,所以 nnn 给了 500050005000(其实是出题人不会别的算法嘿嘿嘿)。

#include<iostream>
#include<stdio.h>
using namespace std;
int T;
int n;
int main()
{
    scanf("%d",&T);

    while(T--)
    {   
        scanf("%d",&n);
        int f=0;
        for(int i=2;i<=n;i++)   f=(f+n-i+1)%i;
        f=(f+1)%n;
        if(f<=0) f+=n;
        printf("%d\n",f);

    }


}

你可能感兴趣的:(bc)