【递推】【NOIP模拟】彩灯的问题 Lights

来源:互联网 发布:五子棋c语言判断胜负 编辑:程序博客网 时间:2024/05/09 14:29

题目叙述

为了庆祝2009NOIP的举办,X市决定举行一次规模宏大的联欢晚会。设计师Tom被邀请负责晚会的灯光设计。
晚会舞台的正上方有n盏可以任意变换颜色的彩灯。它们排列成规则的圆形。为了增加舞台的美感,Tom 决定将任意两盏相邻彩灯设计成不同的颜色。因为演出时还要随时变换彩灯的颜色,所以Tom必须设计出多种方案(只要有一盏对应的彩灯颜色不同,就算两种不同的方案)。
于是一个棘手的问题摆在Tom面前:若有m种颜色,n个彩灯,那么不同的设计方案有多少种呢?因为当n,m较大时,方案数太多,因此他需要你的帮助。
注意:因为每一盏彩灯的位置固定,所以经过旋转或翻转能重合的也算不同的方案。

输入

输入共有一行,两个数:n,m,依次为彩灯的个数与颜色总数。(1n100,1m100)

输出

输出仅有一个数为方案总数。

样例输入

3 4

样例输出

24


先给出自己的很麻烦的递推。
首先这道题一看就是递推
然后要怎么推呢?
简单来说,我们把灯断开拉成一条链,管他第一个灯在哪,就让他随便。
这样的话第一盏灯的颜色有m种情况。
对于每种情况分别计算,但是其实都是一样的,所以只需要计算其中一种然后m输出即可
对于其中的一种,举m=4的情况。
看第一层。
比如说是A,那么第二层一定不能是A,只可能是BCD。
很显然BCD每个下面都会有一个A的可能。
再向下一层,则每个不是A的下面的m1种情况都会有一个A,每个A的下面的m-1种情况都不会是A。
然后推到了第n盏灯。
如果第n1盏灯的颜色是A的话,那么他下面第n盏灯的m1种情况为BCD均满足条件。
如果第n1盏灯的颜色不是A,那么要排除A和他本身,也就是说只剩m2种情况。
总的来说,是第n1盏灯的颜色情况个数乘m2加上A的情况个数。
推完了最后一层所有情况的个数即为最终解。
写个图:【0base】排版错乱将就看

A
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 ……


对于前n1层的每一层要做的无非是总数目和本层A的数目,而其中A的数目就是上一层不是A的数目。
递推:

an+1=(m1)nan,a0=1.

对于本分支答案,
ans=mn2(m2)+an1.

最后乘个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)。 我们设三盏连续的彩灯依次编号为i1ii+1。现在分析彩灯i1与彩灯i+1
    1. 若彩灯i1与彩灯i+1颜色不同,则去掉彩灯i,则变成用m种颜色给n1盏彩灯着色的方案数。即f(i1)。 因为彩灯im2种情况(它与彩灯i1i+1颜色不同),所以此时方案数为(m2)f(i1)
    2. 若彩灯i1与彩灯i+1颜色相同,则去掉彩灯i,并把彩灯i1i+1合并成彩灯i,则变成用m种颜色给i2盏彩灯着色的方案数。即f(i2)。 因为彩灯im1种情况(它与彩灯i1i+1颜色不同),所以此时方案数为(m1)f(i2)
      • 综合1、2得出计算f(n)的递归方程式:
        f(n)=(m2)f(n1)+(m1)f(n2).
  • 边界条件: 由上述分析过程可以看出满足递归式的n必须大于3。故必须先求出f(1)f(2)f(3)的值。很明显:
     
    f(1)=m,f(2)=m(m1),f(3)=m(m1)(m2).
  • 此外,因本题方案数增长很快,因此高精度计算也是必不可少的。
1 0
原创粉丝点击