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

来源:互联网 发布:sdrsharp linux 编辑:程序博客网 时间:2024/05/16 14:37

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;}
阅读全文
0 0
原创粉丝点击