【状态设计的力量】【JZOJ 5411】 友谊

来源:互联网 发布:吉首大学网络授课 编辑:程序博客网 时间:2024/04/29 21:31

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,p<=1e9+7

抽象题意

将奇偶分开,偶一排放上面,奇一排放下面,球有颜色,上下同色匹配(匹配形如“\”或“|”),要求匹配数>=n,求给球赋值颜色的方案数

60%

dp显然
f[i][j][k]表示下面到第i个,上面剩余失配白色数为j,失配黑色数为k
枚举当前第i列上下颜色转移
状态O(n^3)转移O(1)

100%

不dp很难做,着手于优化dp状态
可以发现,有一个约束条件j+k<=n,考虑能否把k那一维省掉
设g[i][j],i,j 含义同上,那么k<=n-j,g[i][j]=njk=0f[i][j][k]
但是这样会算重,如果对于一组序列它最后为匹配的白色数为a,黑色为b
有a<=j && b<=n-j 且 a<=j’ && b<=n-j’
那么这一组序列在g[i][j]与g[i][j’]都会被计算
我们考虑当且仅当a=j 的时候计算,而对于a< j 且也合法的状态就不算了。这样只会被算一次
什么时候a=j呢,那么就应该是中间某个时刻出现了j=0(感受一下)
反证很显然:如果a>j,那么始终有j>0
于是多一维状态表示是否曾经出现了j=0,如果是才计入答案
状态O(n^2)转移O(1)

Code

#include<ctime>#include<cstdio>#include<cstring>#include<algorithm>#define fo(i,a,b) for(int i=a;i<=b;i++)#define fd(i,b,a) for(int i=b;i>=a;i--)#define efo(i,v,u) for(int i=last[v],u=to[i];i;i=next[i],u=to[i])#define max(x,y) ((x)>(y)?(x):(y))#define min(x,y) ((x)<(y)?(x):(y))#define mset(a,x) memset(a,x,sizeof(a))using namespace std;typedef long long ll;typedef double db;char ch;void read(int &n){    n=0;int p=1;    for(ch=getchar();ch<'0' || ch>'9';ch=getchar())        if(ch=='-') p=-1;    for(;'0'<=ch && ch<='9';ch=getchar()) n=n*10+ch-'0';    n*=p;}const int N=3005;int n,m,mo,f[N][N][2];void update(int &x,ll y){x=(ll)(x+y)%mo;}int main(){    freopen("friend.in","r",stdin);    //freopen("friend.out","w",stdout);    read(n),read(m),read(mo);    --n,--m;    fo(i,0,n) f[0][i][(i==0)]=1;    fo(i,0,m)    {        update(f[i+1][0][1],2*f[i][0][1]);//0 0;1 1;        update(f[i+1][1][1],f[i][0][1]);//0 1        fo(j,1,n)        {            update(f[i+1][j][0],2*f[i][j][0]);            update(f[i+1][j][1],2*f[i][j][1]);            //0 0;1 1;            if(j<n)            {                update(f[i+1][j+1][0],f[i][j][0]);                update(f[i+1][j+1][1],f[i][j][1]);            }//0 1            if(j>1)            {                update(f[i+1][j-1][0],f[i][j][0]);                update(f[i+1][j-1][1],f[i][j][1]);            }            else update(f[i+1][j-1][1],f[i][j][0]+f[i][j][1]);            //1 0        }    }    int ans=0;    fo(i,0,n) update(ans,f[m][i][1]);    printf("%d",(ll)ans*4%mo);    return 0;}
原创粉丝点击