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

32 篇文章 0 订阅

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 取模的结果.

Solution

这题的题解讲得不明不白的
这题的做法是先算出带重复的答案,再想办法去掉重复的,

把原来的序列就是01序列嘛,把它拆开成位置是偶数的序列和奇数的序列,
把它拆成上下两个序列,上边的是奇数的(序列a),下边的是偶数的(序列b)(这样好看),
这样,题目就变成了,对于每个位置i, ai 上的数字去匹配 b1 ~ bi1 ,相同即匹配成功,(注意:是到i-1,不是到i)

为了好算,我们试着先保证a全部都要匹配上,那么就在 b1 的前面添加n个虚点,如果 ai 失配了,它就会连到虚点上,这些虚点有多少个0/1我们就先枚举,
这样,a就要保证全部匹配,
所以,b中(包括虚点)一定有n个是没有匹配的,
(看不懂没关系,继续往下看)

设DP: fi,j 表示做到点i,前面有j个0,(n-j)个1,
初始为 f0,0...n=1 ,(枚举0/1分别有多少个)
转移就直分4 种情况,分别枚举 ai,bi 是什么,

fi,j=fi1,j+fi1,j+1+fi1,j1+fi1,j

显然。这样会算重,因为:
比如:n=3,
对于一种转移方案,它用到了虚点中的2个1,
那么对于虚点为0,1,1的情况,它被计算了,
那么对于虚点为1,1,1的情况,它也被计算了,
原因是它只用了2个1,所以剩下那个是什么都没有影响,所以就重复了,

所以,只有当虚点中的0或1被全部用完了,这个状态才能被计算,(这样就不会重了嘛)
当然,如果有虚点有0且非虚点也有0,你强制去匹配虚点的而不是非虚点的,这样强制把虚点用完的不算,

也就是,在转移的过程中,如果有一个状态的j=0(0没了)或j=m(1没了),那么就表示它的虚点以用完了,以后也肯定是用完的(你不能然它吐出来吧),

所以多维护一个状态 fsi,j ,表示转移过来的所有状态中,有多少个是经过了k=0或j=m的,
转移方程与f一样,就是又这么一条:
if(0==j||j==m)fs[i][j]=f[i][j];

最后答案就是 fsn,i 之和

复杂度: O(nm)

Code

#include <cstdio>
#define fo(i,a,b) for(int i=a;i<=b;i++)
using namespace std;
typedef long long LL;
const int N=3500;
int read(int &n)
{
    char ch=' ';int q=0,w=1;
    for(;(ch!='-')&&((ch<'0')||(ch>'9'));ch=getchar());
    if(ch=='-')w=-1,ch=getchar();
    for(;ch>='0' && ch<='9';ch=getchar())q=q*10+ch-48;n=q*w;return n;
}
int m,n,mo,ans;
int f[N][N],fs[N][N];
int main()
{
    freopen("friend.in","r",stdin);
    freopen("friend.out","w",stdout);
    int q,w;
    read(m),read(n),read(mo);
    fo(i,0,m)f[0][i]=1;
    fo(i,1,n)
    {
        fo(j,0,m)
        {
            if(j<m)
            {
                f[i][j]=(f[i-1][j]+f[i-1][j+1]);
                if(f[i][j]>=mo)f[i][j]-=mo;
                fs[i][j]=(fs[i-1][j]+fs[i-1][j+1]);
                if(fs[i][j]>=mo)fs[i][j]-=mo;
            }
            if(j)
            {
                f[i][j]+=f[i-1][j-1];
                if(f[i][j]>=mo)f[i][j]-=mo;
                fs[i][j]+=fs[i-1][j-1];
                if(fs[i][j]>=mo)fs[i][j]-=mo;
                f[i][j]+=f[i-1][j];
                if(f[i][j]>=mo)f[i][j]-=mo;
                fs[i][j]+=fs[i-1][j];
                if(fs[i][j]>=mo)fs[i][j]-=mo;
            }
            if(!j||j==m)fs[i][j]=f[i][j];
        }
    }
    ans=0;
    fo(j,0,m)
    {
        ans+=fs[n][j];
        if(ans>=mo)ans-=mo;
    }
    printf("%d\n",ans);
    return 0;
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值