题目叙述
为了庆祝2009NOIP的举办,X市决定举行一次规模宏大的联欢晚会。设计师Tom被邀请负责晚会的灯光设计。
晚会舞台的正上方有 n 盏可以任意变换颜色的彩灯。它们排列成规则的圆形。为了增加舞台的美感,Tom 决定将任意两盏相邻彩灯设计成不同的颜色。因为演出时还要随时变换彩灯的颜色,所以Tom必须设计出多种方案(只要有一盏对应的彩灯颜色不同,就算两种不同的方案)。
于是一个棘手的问题摆在Tom面前:若有m 种颜色, n 个彩灯,那么不同的设计方案有多少种呢?因为当n , m 较大时,方案数太多,因此他需要你的帮助。
注意:因为每一盏彩灯的位置固定,所以经过旋转或翻转能重合的也算不同的方案。
输入
输入共有一行,两个数: n ,
m ,依次为彩灯的个数与颜色总数。 (1≤n≤100,1≤m≤100)
输出
输出仅有一个数为方案总数。
样例输入
3 4
样例输出
24
先给出自己的很麻烦的递推。
首先这道题一看就是递推。
然后要怎么推呢?
简单来说,我们把灯断开拉成一条链,管他第一个灯在哪,就让他随便。
这样的话第一盏灯的颜色有
m
种情况。
对于每种情况分别计算,但是其实都是一样的,所以只需要计算其中一种然后
对于其中的一种,举
m=4
的情况。
看第一层。
比如说是A,那么第二层一定不能是A,只可能是BCD。
很显然BCD每个下面都会有一个A的可能。
再向下一层,则每个不是A的下面的
m−1
种情况都会有一个A,每个A的下面的m-1种情况都不会是A。
然后推到了第n盏灯。
如果第
n−1
盏灯的颜色是A的话,那么他下面第
n
盏灯的
如果第
n−1
盏灯的颜色不是A,那么要排除A和他本身,也就是说只剩
m−2
种情况。
总的来说,是第
n−1
盏灯的颜色情况个数乘
m−2
加上A的情况个数。
推完了最后一层所有情况的个数即为最终解。
写个图:【0base】排版错乱将就看
B C D
A C D | A B D | A B C
BCD ABD ABC| BCD ACD ABC |BCD ACD ABD
……
…… A B C D ……
…… BCD CD BD BC ……
对于前
n−1
层的每一层要做的无非是总数目和本层A的数目,而其中A的数目就是上一层不是A的数目。
递推:
对于本分支答案,
最后乘个 m 即可。
推着还好吧,然而这样的话相比较答案来说需要的高精度就有加减乘三种了,代码复杂度增加不少,我这种蒟蒻就高精度懵逼了。
其实本质都差不多,这个递推式还蛮简单的只涉及到上一层的情况。
欢迎进行优化。
貌似可以求通项?】【懒了】
上代码:
#include <cstdio>
#include <iostream>
#include <cmath>
#include <vector>
void jin(vector<int> &s){ //统一进位
int j=0;
while(j<s.size()-1){
s[j+1]+=s[j]/10;
s[j]%=10;
j++;
}
while(s[j]>=10){
s.push_back(s[j]/10);
s[j]%=10;
j++;
}
}
void print(vector<int> s){ //打印
for(vector<int>::reverse_iterator it=s.rbegin();it<s.rend();it++)
printf("%d", *it);
printf("\n");
}
vector<int> highMulti(vector<int> s, int m){ //高精乘
int w=s.size();
int i=0;
for(i=0;i<w;i++)
s[i]*=m;
jin(s);
return s;
}
vector<int> highMinus(vector<int> a, vector<int> b){ //高精减
for(int i=0;i<b.size();i++){
if(a[i]<b[i]){
a[i+1]-=1;
a[i]+=10;
}
a[i]-=b[i];
}
return a;
}
vector<int> highPlus(vector<int> a, vector<int> b){ //高精加
int i=0;
for(i=0;i<b.size();i++)
a[i]+=b[i];
jin(a);
return a;
}
int main(){
int n, m, k, w;
scanf("%d%d", &n, &m);
if(n==1&&m==1){ //邪恶的特判
printf("%d\n", 1);
return 0;
}
k=n-1, w=m; //k就是换了个下标不用管
vector<int> wa; //然后把m高精度存进这个wa去
while(w){
wa.push_back(w%10); //倒着存
w/=10;
}
vector<int> a; //数列a
a.push_back(1);
vector<int> mi; //m的幂次
mi.push_back(1);
for(int i=0;i<k-1;i++){
a=highMinus(mi, a); //a[i]=m^(i-1)-a[i-1]
mi=highMulti(mi, m-1); //mi[i]=mi[i-1]*(m-1)
}
vector<int> res=highMulti(highPlus(highMulti(mi, m-2), a), m);
//res=(mi*(m-2)+a)*m;
print(res);
return 0;
}
总的来说还好吧,嗯大概就是这样。
标算
- 设
f(i) 为用 m 种颜色给i 盏彩灯着色的方案数, (i=1,2,...n) 。 我们设三盏连续的彩灯依次编号为 i−1 , i ,i+1 。现在分析彩灯 i−1 与彩灯 i+1 。
- 若彩灯
i−1
与彩灯
i+1
颜色不同,则去掉彩灯
i
,则变成用
m 种颜色给 n−1 盏彩灯着色的方案数。即 f(i−1) 。 因为彩灯 i 有m−2 种情况(它与彩灯 i−1 , i+1 颜色不同),所以此时方案数为 (m−2)∗f(i−1) 。 - 若彩灯
i−1
与彩灯
i+1
颜色相同,则去掉彩灯
i
,并把彩灯
i−1 , i+1 合并成彩灯 i′ ,则变成用 m 种颜色给i−2 盏彩灯着色的方案数。即 f(i−2) 。 因为彩灯 i 有m−1 种情况(它与彩灯 i−1 , i+1 颜色不同),所以此时方案数为 (m−1)∗f(i−2) 。
- 综合1、2得出计算
f(n)
的递归方程式:
f(n)=(m−2)∗f(n−1)+(m−1)∗f(n−2).
- 综合1、2得出计算
f(n)
的递归方程式:
- 边界条件: 由上述分析过程可以看出满足递归式的
n
必须大于
3 。故必须先求出 f(1) 、 f(2) 、 f(3) 的值。很明显:
f(1)=m,f(2)=m∗(m−1),f(3)=m∗(m−1)∗(m−2). - 此外,因本题方案数增长很快,因此高精度计算也是必不可少的。
- 若彩灯
i−1
与彩灯
i+1
颜色不同,则去掉彩灯
i
,则变成用