拯救创世山

题目描述

给出n层每层l[i]个村庄,m个颜色,求总共有几种颜色分配方案使得每个相邻村庄颜色不同,相邻两层颜色集合不同,答案%=p.

数据范围

对于100%的数据,n,m<=1e6,l[i]<=5e3.∑l[i]<=1e7,p<=1e9.

解析

使用动态规划。

只考虑一层先预处理出g[i][j]表示前i个村庄选用了j个颜色方案数,默认颜色第一次使用位置升序排列(之后使用g时对其乘以j!即是所有排列方法)。

设f[i][j]表示处理到前i层,sum[i]=∑f[i][j],第i层用了j种颜色的方案数.f[i][j]为该行不考虑上一行方案数再减去与上一行颜色完全相同的方案数。即f[i][j]=sum[i-1]*g[l[i]][j]*Сj.m,*j!.

如果j<=l[i-1] f[i][j]-=f[i-1][j]*g[l[i]][j]*j!;

Cj,m*j! 就等于Pj,m,和j!均可以预处理得出。由于∑l[i]<=1e7所以复杂度为1e7,可以通过。

卡常卡得恼火,预处理g是不能开long long,需要在里头转换数据为long long.

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N=1e6+5; 
int yc[5005][5005],n,p;
int l[N],m;
ll cj[N],f[2][5005],jc[5005],ans,sum[2];
inline int read()
{
    char c=getchar();
    int x=0;
    while(!isdigit(c)) c=getchar();
    while(isdigit(c)) 
    {
        x=(x<<3)+(x<<1)+c-'0';
        c=getchar();
    }
    return x;
}
int main()
{
    n=read();m=read();p=read();
    const int mo=p;
    cj[0]=1;
    jc[1]=1;
    yc[0][0]=1;
    for(int i=2;i<=5000;i++) jc[i]=(jc[i-1]*i)%mo;
    for(int i=1;i<=m;i++) cj[i]=(cj[i-1]*(m-i+1))%mo;
    for(int i=1;i<=5000;i++)
        for(int j=1;j<=5000;j++)
        {
            yc[i][j]=(((long long)yc[i-1][j-1]+(long long)((long long)yc[i-1][j]*(long long)(j-1)))%(long long)mo);
        }
    for(int i=1;i<=n;i++) l[i]=read();
    f[0][0]=1;sum[0]=1;
    for(int i=1;i<=n;i++)
    {
        int hu=i%2;sum[hu]=0;
        for(int j=1;j<=min(l[i],m);j++)
        {
            f[hu][j]=sum[hu^1]*(long long)yc[l[i]][j]%mo*cj[j]%mo;
            if(j<=l[i-1])
            f[hu][j]=(f[hu][j]-(f[hu^1][j]*(long long)yc[l[i]][j]%mo*jc[j]%mo)%mo+mo)%mo;
            sum[hu]=(sum[hu]+f[hu][j])%mo;        
        }
    }
    for(int i=1;i<=min(l[n],m);i++) ans=(ans+f[n%2][i])%mo;    
    cout<<ans;
    return 0;
}
//1 613980 274686798
//5000
原文地址:https://www.cnblogs.com/betablewaloot/p/12130207.html