codeforces 587B Duff in Beach (dp)

B. Duff in Beach

While Duff was resting in the beach, she accidentally found a strange array b0, b1, ..., bl - 1 consisting of lpositive integers. This array was strange because it was extremely long, but there was another (maybe shorter) array, a0, ..., an - 1 that b can be build from a with formula: bi = ai mod n where a mod b denoted the remainder of dividing a by b.

Duff is so curious, she wants to know the number of subsequences of b like bi1, bi2, ..., bix(0 ≤ i1 < i2 < ... < ix < l), such that:

  • 1 ≤ x ≤ k
  • For each 1 ≤ j ≤ x - 1
  • For each 1 ≤ j ≤ x - 1bij ≤ bij + 1. i.e this subsequence is non-decreasing.

Since this number can be very large, she want to know it modulo 109 + 7.

Duff is not a programmer, and Malek is unavailable at the moment. So she asked for your help. Please tell her this number.

Input

The first line of input contains three integers, n, l and k (1 ≤ n, kn × k ≤ 106 and 1 ≤ l ≤ 1018).

The second line contains n space separated integers, a0, a1, ..., an - 1 (1 ≤ ai ≤ 109 for each 0 ≤ i ≤ n - 1).

Output

Print the answer modulo 1 000 000 007 in one line.


解题思路:

首先比较容易发现的特性就是既然每一段都只能取一个数字,所以在完整的一段内数字的次序其实不重要,所以我们可以将数字排序,这会方便我们之后的一些操作。

定义 dp[i][j] = 以编号为 j 的数字结尾的长度为 i 的序列有多少个

所以转移就相当于 dp[i][j] =  ∑ dp[i-1][ 0-w ] w为小于等于编号j的数字的最大的编号

这样编号0-w的数字都一定小于第j个数字  这样我们就能通过之前的到的长度为 i - 1 的序列得到长度为 i 的序列。

到这里这道题完成了第一步,下一步需要考虑答案的组成。

先不考虑 n不能整除l 的时候

考虑 l 非常大的时候,长度为 i 的序列其实可能出现很多次 次数等于 l / n - i + 1 所以需要将这个倍数和对应位置的dp值乘在一起。

考虑 n不能整除l 的时候

单独考虑剩余的那些数,其实只需要计算以他们为结尾,长度为 1,2,3,。。。的序列的个数就可以了~

AC代码:

/*
* @Author: wchhlbt
* @Last Modified time: 2017-10-02
*/
#include <bits/stdc++.h>

#define inf 0x3f3f3f3f
#define pb push_back
#define AA first
#define BB second
#define ONES(x) __builtin_popcount(x)
#define _  << "  " <<
using namespace std;

typedef pair<int, int> P;
typedef long long ll ;
int dx[4] = {0,0,1,-1};
int dy[4] = {1,-1,0,0};
const double eps =1e-8;
const int mod = 1000000007;
const double PI = acos(-1.0);
inline int read(){ int num;    scanf("%d",&num);   return num;}
const int maxn = 1000007;

int a[maxn];
int b[maxn];
int pos[maxn];//记录数字所对应的最大编号在哪里

vector<ll> dp[maxn];//dp[i][j] 表示 以编号j的数字为结尾的长度为i的序列的个数
map<int,int> id;
ll ans;

int main()
{
	ll n,k,l;
	cin>>n>>l>>k;
	for(int i = 0; i<n; i++){
		a[i] = read();
		b[i] = a[i];
	}
	sort(b,b+n);
	for(int i = 0; i<n; i++)	id[b[i]] = i;	//记录一下重排之后数字的编号
	ll m = min(l/n,k);
	int res = l%n;
	for(int i = 0; i<n; i++){
		dp[1].pb(1);
		pos[i] = id[b[i]];
	}

	for(int i = 2; i<=m+1; i++){	//计算完整部分的dp值,这里最好算到m+1层
		ll s = 0;	int x = 0;
		for(int j = 0; j<n; j++){
			while(x<=pos[j])	s = (s + dp[i-1][x++])%mod;
			dp[i].pb(s);
		}
	}

	for(int i = 1; i<=m; i++)
		for(int j = 0; j<n; j++)
			ans = (ans + dp[i][j] * ((l/n-i+1)%mod) )%mod; //注意乘法和取模的优先级

	for(int i = 0; i<res; i++){					//处理剩余的部分
		int o = id[a[i]];
		for(int j = 1; j<=min(k,l/n+1); j++){	//注意这里层数的上界
			ans = (ans + dp[j][o])%mod;
		}
	}
	cout << ans << endl;
    return 0;
}



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
引用\[1\]中提到了一种树形动态规划的方法来解决CodeForces - 982C问题。在这个问题中,subtree指的是子连通块,而不是子树。为了使cnt_white - cnt_black尽可能大,可以使用两次树形动态规划来求解。第一次是自底向上的过程,维护一个dp数组,表示以每个节点为根的子树中的最大连通块。第二次是自顶向下的过程,处理自底向上过程中无法包含的树链所代表的子树。在第二次遍历中,需要维护一个sum变量,用于存储树链所代表的子树的贡献。根据ans\[u\]的正负,决定是否能对相邻的子节点做出贡献。如果ans\[u\]为正,则减去dp\[v\]就是树链所代表的子树的权值。最终,ans\[u\]代表包含节点u在内的子连通块的最大权值。\[1\] 问题: CodeForces - 982C 树形DP是什么问题?如何解决? 回答: CodeForces - 982C是一个树形动态规划问题。在这个问题中,需要求解子连通块的最大权值和,使得cnt_white - cnt_black尽可能大。解决这个问题的方法是使用两次树形动态规划。第一次是自底向上的过程,维护一个dp数组,表示以每个节点为根的子树中的最大连通块。第二次是自顶向下的过程,处理自底向上过程中无法包含的树链所代表的子树。在第二次遍历中,需要维护一个sum变量,用于存储树链所代表的子树的贡献。根据ans\[u\]的正负,决定是否能对相邻的子节点做出贡献。最终,ans\[u\]代表包含节点u在内的子连通块的最大权值。\[1\] #### 引用[.reference_title] - *1* *2* [CodeForces - 1324F Maximum White Subtree(树形dp)](https://blog.csdn.net/qq_45458915/article/details/104831678)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^koosearch_v1,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值