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

本文探讨了一个特定的动态规划问题,通过巧妙地优化原有算法来减少重复计算,最终达到高效求解的目标。文中详细介绍了如何通过增加额外维度来避免状态重复,并提供了一段完整的C++实现代码。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Description

一个长度为2m的序列,每个位置可以是0或1
任意两个i < j,i为偶数,j为奇数的0/1相同的位置可以匹配,每个位置最多匹配一次
求有多少个序列满足匹配完剩余的位置<=2n
答案对p取模
n,m<=3000,p<=1e9+7

Solution

一道吼题,正解有些猎奇
首先考虑mn^2Dp,这个相信大家都会,设Fi,j,k表示前i个位置,剩余j个可匹配0,k个可匹配1的方案
如何优化?
我们考虑在最前面多放上n个可匹配的位置,这样合法状态一定没有剩余
并且发现可供匹配的位置数量恒为n
那么我们就可以设Fi,j表示前i个位置,有j个可匹配0的方案(此时有n-j个可匹配1)
但是这样会算重?!
我们发现如果我们添加的某种供给匹配点如果没有用完的话这个状态就会被算重
所以多加一维表示这个状态是否经历过j=0,即可匹配0用完的方案
这样就可以通过本题

Code

#include <cstdio>
#include <cstring>
#include <algorithm>
#define fo(i,a,b) for(int i=a;i<=b;i++)
using namespace std;

typedef long long ll;
int read() {
    char ch;
    for(ch=getchar();ch<'0'||ch>'9';ch=getchar());
    int x=ch-'0';
    for(ch=getchar();ch>='0'&&ch<='9';ch=getchar()) x=x*10+ch-'0';
    return x;
}

int n,m,p;
void inc(int &x,int y) {
    if (y>=p) y=y-p;
    x=x+y;
    if (x>=p) x=x-p;
}
void init() {
    n=read();m=read();p=read();
}

#define min(x,y) (x<y?x:y)
const int N=3*1e3+5;
int f[N][N][2],ans;
void solve() {
    fo(i,1,n) f[0][i][0]=1;
    f[0][0][1]=1;
    fo(i,0,m-1) 
        fo(j,0,n)
            fo(z,0,1)
                if (f[i][j][z]) 
                    if (j>1&&j<n) {
                        inc(f[i+1][j-1][!(j-1)||z],f[i][j][z]);
                        inc(f[i+1][j+1][z],f[i][j][z]);
                        inc(f[i+1][j][z],f[i][j][z]*2);
                    } else if (!j) {
                        inc(f[i+1][j][z],f[i][j][z]);
                        inc(f[i+1][j+1][z],f[i][j][z]);
                    } else if (j==1) {
                        inc(f[i+1][j][1],f[i][j][z]);
                        inc(f[i+1][j-1][!(j-1)||z],f[i][j][z]);
                        if (n!=1) {
                            inc(f[i+1][j+1][z],f[i][j][z]);
                            inc(f[i+1][j][z],f[i][j][z]);
                        }
                    } else {
                        inc(f[i+1][j][z],f[i][j][z]);
                        inc(f[i+1][j-1][z],f[i][j][z]);
                    }
    fo(i,0,n) inc(ans,f[m][i][1]);
    printf("%d\n",ans);
}
int main() {
    freopen("friend.in","r",stdin);
    freopen("friend.out","w",stdout);
    init();solve();
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值