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