NC17621 管道取珠(模型转换+DP)

题目链接

题意:
有 两 个 管 道 , 管 道 1 有 n 个 球 , 管 道 2 有 m 个 球 有两个管道,管道1有n个球,管道2有m个球 1n2m
球 有 A 和 B 两 种 颜 色 球有A和B两种颜色 AB
从 A 或 B 分 别 取 球 , 可 能 有 C ( n + m , n ) 种 方 案 从A或B分别取球,可能有C(n+m,n)种方案 ABC(n+m,n)
这 些 方 案 中 可 能 有 取 出 球 序 列 相 同 的 这些方案中可能有取出球序列相同的
假 设 总 共 有 k 种 不 同 的 球 序 列 , a i 为 每 种 的 方 案 数 假设总共有k种不同的球序列,a_i为每种的方案数 kai
那 么 ∑ i = 1 k a i = C ( n + m , n ) 那么\sum_{i=1}^k a_i=C(n+m,n) i=1kai=C(n+m,n)
求 ∑ i = 1 k a i 2   m o d   1024523 求\sum_{i=1}^k a_i^2~mod ~1024523 i=1kai2 mod 1024523
题解:
n , m < = 500 n,m<=500 n,m<=500
求 ∑ i = 1 k a i 可 以 直 接 得 出 求\sum_{i=1}^k a_i可以直接得出 i=1kai
但 是 要 求 求 的 结 果 是 ∑ i = 1 k a i 2 但是要求求的结果是\sum_{i=1}^k a_i^2 i=1kai2
如 果 这 道 题 ( n + m ) 比 较 小 , 可 以 考 虑 二 进 制 枚 举 如果这道题(n+m)比较小,可以考虑二进制枚举 (n+m)
对 于 每 种 进 行 平 方 求 和 , 但 是 这 里 n , m 太 大 , 肯 定 不 够 用 对于每种进行平方求和,但是这里n,m太大,肯定不够用 nm
所 以 我 们 就 可 以 把 问 题 转 化 所以我们就可以把问题转化
一 般 对 于 k 次 方 , 你 就 可 以 把 他 转 化 成 k 个 人 玩 这 个 游 戏 一般对于k次方,你就可以把他转化成k个人玩这个游戏 kk
就 比 如 这 道 题 , 是 两 次 方 , 所 以 转 化 成 2 个 人 玩 这 个 游 戏 就比如这道题,是两次方,所以转化成2个人玩这个游戏 2
然 后 求 这 两 个 人 序 列 相 同 的 方 案 数 然后求这两个人序列相同的方案数
第 一 个 拿 到 这 个 序 列 的 方 案 数 是 a i , 第 二 个 人 是 b i 第一个拿到这个序列的方案数是a_i,第二个人是b_i ai,bi
那 么 两 个 人 同 时 拿 到 这 个 序 列 的 方 案 数 就 是 a i ∗ b i 那么两个人同时拿到这个序列的方案数就是a_i*b_i aibi
对 于 这 道 题 中 , 游 戏 相 同 , 初 始 相 同 , 所 以 a i = = b i 对于这道题中,游戏相同,初始相同,所以a_i==b_i ai==bi

所 以 现 在 需 要 考 虑 的 就 是 2 个 人 玩 这 个 游 戏 序 列 相 同 的 方 案 数 所以现在需要考虑的就是2个人玩这个游戏序列相同的方案数 2
这 样 就 很 明 显 了 , 应 该 是 用 d p 来 求 这样就很明显了,应该是用dp来求 dp
然 后 开 始 构 建 d p 的 模 型 然后开始构建dp的模型 dp
状 态 应 该 就 是 两 个 人 各 自 从 两 个 管 道 拿 的 球 序 列 相 同 的 方 案 状态应该就是两个人各自从两个管道拿的球序列相同的方案
d p [ i ] [ j ] [ k ] [ l ] 表 示 第 一 个 人 从 管 道 1 拿 i 个 , 管 道 2 拿 j 个 dp[i][j][k][l]表示第一个人从管道1拿i个,管道2拿j个 dp[i][j][k][l]1i2j
第 二 个 人 从 管 道 1 拿 k 个 , 管 道 2 拿 l 个 两 人 序 列 相 同 的 方 案 第二个人从管道1拿k个,管道2拿l个两人序列相同的方案 1k2l
但 是 四 维 D P 不 论 时 间 空 间 都 不 允 许 但是四维DP不论时间空间都不允许 DP
考 虑 一 下 降 维 , 我 们 会 发 现 , 两 个 人 序 列 相 同 , 拿 的 个 数 肯 定 一 样 考虑一下降维,我们会发现,两个人序列相同,拿的个数肯定一样
所 以 i + j = = k + l , 那 么 就 可 以 去 掉 l 这 一 维 所以i+j==k+l,那么就可以去掉l这一维 i+j==k+ll

然 后 考 虑 状 态 转 移 , 由 于 是 拿 的 个 数 然后考虑状态转移,由于是拿的个数
我 们 考 虑 的 应 该 是 往 下 一 个 状 态 转 移 我们考虑的应该是往下一个状态转移
如 果 第 一 个 人 管 道 1 ( 管 道 2 ) 的 当 前 球 和 第 二 个 人 管 道 2 的 当 前 球 一 样 如果第一个人管道1(管道2)的当前球和第二个人管道2的当前球一样 122
那 么 就 可 以 让 这 两 个 人 分 别 拿 相 同 球 , 并 得 到 当 前 状 态 的 贡 献 那么就可以让这两个人分别拿相同球,并得到当前状态的贡献
这 样 就 可 以 形 成 状 态 转 移 ( 详 细 见 下 代 码 ) 这样就可以形成状态转移(详细见下代码)

然 后 可 以 看 到 , 这 个 递 推 永 远 是 往 下 一 个 状 态 转 移 然后可以看到,这个递推永远是往下一个状态转移
也 就 是 球 数 增 长 的 方 向 , 不 需 要 前 一 步 的 值 也就是球数增长的方向,不需要前一步的值
这 样 的 话 , 对 于 最 外 层 循 环 那 一 维 , 就 可 以 使 用 滚 动 数 组 这样的话,对于最外层循环那一维,就可以使用滚动数组 使

最 后 得 到 d p [ n ] [ m ] [ n ] 即 可 最后得到dp[n][m][n]即可 dp[n][m][n]

AC代码

/*
    Author:zzugzx
    Lang:C++
    Blog:blog.csdn.net/qq_43756519
*/
#include<bits/stdc++.h>
using namespace std;
#define fi first
#define se second
#define pb push_back
#define mp make_pair
#define all(x) (x).begin(),(x).end()
#define endl '\n'
typedef long long ll;
typedef pair<int, int> pii;
typedef pair<ll, ll> pll;
const int mod=1024523;
//const int mod=998244353;
const double eps = 1e-10;
const double pi=acos(-1.0);
const int maxn=1e6+10;
const ll inf=0x3f3f3f3f;
const int dir[4][2]={{0,1},{1,0},{0,-1},{-1,0}};

int n,m;
int dp[2][510][510];
string a,b;
int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);cout.tie(0);
    //freopen("in.txt","r",stdin);
    //freopen("out.txt","w",stdout);
    cin>>n>>m;
    cin>>a>>b;a='.'+a,b='.'+b;
    dp[0][0][0]=1;
    int cur=0;
    for(int i=0;i<=n;i++,cur^=1)
        for(int j=0;j<=m;j++)
            for(int k=0;k<=n;k++){
                int l=i+j-k;
                int &x=dp[cur][j][k];
                if(l<0||l>m)continue;
                if(a[i+1]==a[k+1])dp[cur^1][j][k+1]=(dp[cur^1][j][k+1]+x)%mod;
                if(a[i+1]==b[l+1])dp[cur^1][j][k]=(dp[cur^1][j][k]+x)%mod;
                if(b[j+1]==a[k+1])dp[cur][j+1][k+1]=(dp[cur][j+1][k+1]+x)%mod;
                if(b[j+1]==b[l+1])dp[cur][j+1][k]=(dp[cur][j+1][k]+x)%mod;
                x=0;
            }
    cout<<dp[cur][m][n];
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值