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_isi 是否在 [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] =
此题只用在原版约瑟夫问题上加一维,由于依次隔 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] =
答案就是 f[n][1]+1f[n][1] + 1f[n][1]+1(将标号转移到 [1,n][1, n][1,n]),问题轻松解决。
复杂度:预处理 O(n2)O(n^2)O(n2),查询 O(1)O(1)O(1),总复杂度 O(n2)O(n^2)O(n2)。由于可以滚动数组以及常数太小,所以 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);
}
}