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; }