【JZOJ5411】【NOIP2017提高A组集训10.22】友谊

Description

Flowey 是一朵能够通过友谊颗粒传播LOVE 的小花.它的友谊颗粒分为两种,
圆粒的和皱粒的,它们依次排列组成了一个长度为2m 的序列.对于一个友谊颗
粒的序列,如果存在1<=i

Data Constraint

对于30%的数据,满足n<=10,m<=10
对于60%的数据,满足n<=300,m<=300
对于100%的数据,满足1<=n<=3000,1<=m<=3000,2<=p<=1000000007

Solution

我们先讲讲60分的解法。设f[i][j][k]表示前i位的偶数位中还有j个圆粒,k个皱粒没有匹配。那么转移就很显然了。
那么我们现在想想满分:我们将奇数位与偶数位分开。显然1号位和2*m号位不可能匹配,我们将他们去掉,最后在答案上*4即可。为了保证原本不能匹配的奇数位也能匹配,我们在偶数位前人为加多n位,里面事先放入0~n个圆粒,其余为皱粒,即f[0][i]=1。若奇数位不能匹配就找它。那么现在我们设f[i][j]表示奇数位枚举至i,偶数位枚举至n+i,偶数位还有j个圆粒没有匹配,因为奇数位的i个都是匹配的,所以偶数位中只有n+i-i个豌豆没有匹配,又其中有j个圆粒,那自然有n-j个皱粒。那么现在f[i][j][k]就变为f[i][j][n-j]了,转移不变。但我们发现这样的计算会算重。即加入的n个偶数位中假如有a个圆粒,b个皱粒,其余的偶数位缺c个圆粒,d个皱粒。那么现在只要a>=c,b>=d答案就会计算一遍,这显然不行。所以我们强制a=c,b>=d时才会计算,即在某一时刻该数组会经历f[i][0]的情况,为了实现这个,我们多加入一位f[i][j][0..1]表示数组是否经历f[i][0],最后答案就为 ni=0f[m][i][1]

Code

#include<iostream>
#include<cmath>
#include<cstring>
#include<cstdio>
#include<algorithm>
using namespace std;
const int maxn=3e3+5;
int f[maxn][maxn][2];
int n,m,mo,i,t,j,k,l,x,y,z,ans;
int main(){
    freopen("friend.in","r",stdin);freopen("friend.out","w",stdout);
    scanf("%d%d%d",&n,&m,&mo);n--;m--;
    for (i=1;i<=n;i++)f[0][i][0]=1;f[0][0][1]=1;
    for (i=0;i<m;i++)
        for (j=0;j<=n;j++){
            f[i+1][j][0]=(f[i+1][j][0]+f[i][j][0]*2%mo)%mo;
            f[i+1][j+1][0]=(f[i+1][j+1][0]+f[i][j][0])%mo;
            f[i+1][j][1]=(f[i+1][j][1]+f[i][j][1]*2%mo)%mo;
            f[i+1][j+1][1]=(f[i+1][j+1][1]+f[i][j][1])%mo;
            if (j){
                f[i+1][j-1][1]=(f[i+1][j-1][1]+f[i][j][1])%mo;
                if (j!=1) f[i+1][j-1][0]=(f[i+1][j-1][0]+f[i][j][0])%mo;
                else f[i+1][0][1]=(f[i+1][0][1]+f[i][j][0])%mo;
            }
        }
    for (i=0;i<=n;i++)
        ans=(ans+f[m][i][1])%mo;
    ans=ans*2%mo*2%mo;
    printf("%d\n",ans);
}
发布了257 篇原创文章 · 获赞 451 · 访问量 10万+

猜你喜欢

转载自blog.csdn.net/crybymyself/article/details/78325361