codeforces#326-D - Duff in Beach- dp+分块

来源:互联网 发布:数据库代理 编辑:程序博客网 时间:2024/06/05 03:35

http://codeforces.com/contest/588/problem/D 

题意给你  n, l and k (1 ≤ n, k, n × k ≤ 1^6 and 1 ≤ l ≤ 10^18).

给你n个数的数组a

然后  b数组就是 长度为l  由a数组不断重复拼接得到的数组

要你 从中选择一段合法子序列,求合法子序列的总数

条件如下:

1.这个序列的长度大于等于1,小于等于k

2.这个序列在每一个块中只能选择一个数,并且都必须选择在连续的块中 

3.这个序列是非递减的

题解:

tm原始数据排序后得到的数组

dp[i][j] 表示//dp[i][j]表示 tm[i]结尾的长度为j的方案数

考虑到n,k的范围,我们不能直接开数组。。就用个vector吧

//用vector来存dp, vector[i][j]表示 tm[i]结尾的长度为j的方案数

考虑转移方程

对与当前长度为j的方案数 时:

  以第i个数为结尾,长度为j 的方案数 =   (所有比tm[i]小的数) 的长度为j-1 的方案数之和 

for 一遍 o(n)就得到了长度为j 的方案数 了,然后 再对j  for一遍, o(n*k)得到所有方案数

****************************

然后  提供的最多可选择的块数  是len= l/n 块

我们选择 长度为 j  的子序列时, 因为每块选一个数,并且要选的必须是连续的块

显然 我们只有 len-j+1种 选法,并且每种选法都是合法的

所以

for (i=1;i<=l/n&&i<=k;i++)//因为必须选择连续的块,总共有l/n个块,所以可以选连续i个块 的方案数为 l/n-i+1
{
ans+=(l/n-i+1)%mod*sum[i];  //注意l/n-i+1要先mod一下,防止乘法溢出
ans%=mod;
}


最后 对 最后一段判断一下是否有 多出l%n,暴力判断一下就好了







#include <cstdio>#include <cmath>#include <cstring>#include <string>#include <algorithm>#include <iostream>#include <queue>#include <map>#include <set>#include <vector>using namespace std; __int64 tm[1000005];//排序后的数组__int64 sum[1000005];//长度为i的总方案数__int64 bb[1000005];//原始数组__int64 max(__int64 a,__int64 b){return a<b?b:a;}const __int64 mod= 1000000007;vector <__int64> sb[1000005];//用vector来存dp, vector[i][j]表示 tm[i]结尾的长度为j的方案数map<__int64,__int64>  up;//映射原始数组和排序数组int main(){  __int64 n;__int64 i,j;__int64 l,k;scanf("%I64d%I64d%I64d",&n,&l,&k);for (i=1;i<=n;i++){scanf("%I64d",&tm[i]);bb[i]=tm[i];}sort(tm+1,tm+1+n);for (i=1;i<=n;i++)up[tm[i]]=i;for (i=1;i<=n;i++){sb[i].push_back(0);//去掉第一个位置,从1开始sb[i].push_back(1);}for (j=2;j<=k;j++){__int64 tot=1;for (i=1;i<=n;i++){__int64 p=upper_bound(tm+1,tm+1+n,tm[i])-(tm+1);//找到比tm[i]小的个数while(tot<=p){sum[j-1]+= sb[tot][j-1];//所有比tm[i]小的数的长度为j-1的方案,加上tm[i],就得到了以tm[i]结尾长度为j的方案数了 sum[j-1]%=mod;tot++;}sb[i].push_back(sum[j-1]);//把得到的结果存入 tm[i]结尾长度为j的 dp位置中}}for (i=1;i<=n;i++)//求一下sum[k]:长度为k的总方案数{sum[k]+=sb[i][k];sum[k]%=mod;} __int64 ans=0;for (i=1;i<=l/n&&i<=k;i++)//因为必须选择连续的块,总共有l/n个块,所以可以选连续i个块 的方案数为 l/n-i+1{ans+=(l/n-i+1)%mod*sum[i];  //注意l/n-i+1要先mod一下,防止乘法溢出ans%=mod;}if (l%n)//如果最后有剩余不完整的一小段,则对以该 数结尾的 所有长度小于k的方案数 都要加上一个 dp[i][len]: len=[1,k]{for (i=1;i<=l%n;i++){for (j=1;j<=k&&j<=(l/n+1);j++){__int64 idx= up[bb[i]];ans+=sb[idx][j];ans%=mod;}}}printf("%I64d\n",ans); return 0;}


0 0
原创粉丝点击