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

Description

Flowey 是一朵能够通过友谊颗粒传播LOVE 的小花.它的友谊颗粒分为两种,
圆粒的和皱粒的,它们依次排列组成了一个长度为2m 的序列.对于一个友谊颗
粒的序列,如果存在 1<=i<j<=2m ,满足以下条件:
1)i 为偶数,j 为奇数
2)第i 颗友谊颗粒和第j 颗友谊颗粒同为圆粒或同为皱粒
3)第i 颗友谊颗粒和第j 颗友谊颗粒都还没有被使用过
那么,就可以使用这两颗友谊颗粒,然后提升一次LV.
定义一个友谊颗粒的序列为高效的,当且仅当尽可能多的提升LV 后,序列
上剩余的友谊颗粒数量不超过2n。
现在,Flowey 想知道,长度为2m 的友谊颗粒序列,有多少个不同的序列是
高效的?
定义两个友谊颗粒序列是不同的,当且仅当存在1<=i<=2m,第i 颗友谊颗粒在
一个序列中为圆粒,而在另一个中为皱粒.
由于答案可能很大,你只需要求出答案对p 取模的结果.
对于60%的数据,满足n<=300,m<=300
对于100%的数据,满足1<=n<=3000,1<=m<=3000,2<=p<=1000000007

Solution

显然第一个和最后一个根本不能造成影响,直接去掉,n-1,m-1最后答案乘4
下面讨论都是去掉之后的

先考虑60%

设F[i][j][k]表示做到第i个点,偶数位上还剩j个圆粒,k个皱粒,直接转移即可。

统计答案时偶数位上的剩下的粒数小于n即可

考虑满分做法
可以将原序列看作一个二分图,偶数在上,奇数在下,每次做一列。

既然失配数不能超过N,那么我们在偶数列前面强行添加n个虚点,可圆可皱,不论排列,奇数点和它们匹配相当于失配。

那么现在DP时的失配已经和原来意义不同了,因为凭空多出n个虚点,无论怎么匹配,DP时的失配数始终是n,并且原本的失配数(前后两个失配不一样)一定不会超过n,因为前面的虚点最多只有n个

那么可以设 G[i][j] 表示做到第i列,偶数位上j个圆粒,那么剩下的皱粒个数就是 nj

添加的虚点实际上就是 G[i][0 ~ n]=1

下面来自某位巨犇LYD729的转化思想
由于其比较易懂,根据自己的理解又补充了一些,一起写在下面

或者说,设虚点的意义就是设了
G[i][j]=k=0njF[i][j][k]

但是这样会重复

对于某一个 F[i][x][y]

若存在 j>=x,nj>y
那么它至少在 G[i][j],G[i][j+1] 都算过

若在转移过程中,j始终大于0,那么一定会有至少一个x被算多了一次。

那么我们要求转移过程中j至少等于过0

于是我们增加一维 0/1 ,来表示其是否经历j=0

统计答案时只统计经历过的

Code

 #include <cstdio>
 #include <cstdlib>
 #include <algorithm>
 #include <cstring>
 #include <cmath>
 #include <iostream>
 #define fo(i,a,b) for(int i=a;i<=b;i++)
 #define fod(i,a,b) for(int i=a;i>=b;i--)
 #define N 3005
 #define LL long long
 using namespace std;
 int n,m;
 LL mo,f[N][N][2];
 int main()
 {
    cin>>n>>m>>mo;
    fo(i,0,n-1) f[0][i][i==0]=1;
    fo(i,1,m-1)
    {
        fo(j,0,n-1)
        {
            if(j==0) f[i][j][1]=((f[i-1][j][0]+f[i-1][j][1])*(LL)2+f[i-1][j+1][0]+f[i-1][j+1][1]+((j>0)?(f[i-1][j-1][0]+f[i-1][j-1][1]):0))%mo;
            else
            {
                f[i][j][1]=(f[i-1][j][1]*(LL)2+f[i-1][j+1][1]+((j>0)?f[i-1][j-1][1]:0))%mo;
                f[i][j][0]=(f[i-1][j][0]*(LL)2+f[i-1][j+1][0]+((j>0)?f[i-1][j-1][0]:0))%mo;
            }
        }
    }
    LL ans=0;
    fo(i,0,n-1) (ans+=f[m-1][i][1])%=mo;
    printf("%lld\n",ans*(LL)4%mo);
 }
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值