BestCoder Round #74

来源:互联网 发布:vue vendor.js 编辑:程序博客网 时间:2024/05/19 19:13

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);    }}
1 0