PDF (English) | Statistics | Forum |
Time Limit: 3 second(s) | Memory Limit: 64 MB |
An increasing subsequence from a sequence A1, A2 ... An is defined by Ai1, Ai2 ... Aik, where the following properties hold
1. i1 < i2 < i3 < ... < ik and
2. Ai1 < Ai2 < Ai3 < ... < Aik
Now you are given a sequence, you have to find the number of all possible increasing subsequences.
Input
Input starts with an integer T (≤ 10), denoting the number of test cases.
Each case contains an integer n (1 ≤ n ≤ 105) denoting the number of elements in the initial sequence. The next line will contain n integers separated by spaces, denoting the elements of the sequence. Each of these integers will be fit into a 32 bit signed integer.
Output
For each case of input, print the case number and the number of possible increasing subsequences modulo 1000000007.
Sample Input |
Output for Sample Input |
3 3 1 1 2 5 1 2 1000 1000 1001 3 1 10 11 |
Case 1: 5 Case 2: 23 Case 3: 7 |
Notes
1. For the first case, the increasing subsequences are (1), (1, 2), (1), (1, 2), 2.
2. Dataset is huge, use faster I/O methods.
很棒的一个题,第一次做不是“纯粹”的树状数组,看过题之后一点思路没有,记得以前做过一个dp,也是这样的,只不过数据较小,那时候一顿狂推状态方程,现在也忘光了。。。但是知道这题的思路之后感觉树状数组太奇妙了。。。题意:给你一串数字问你这里面有几个严格上升子序列,自己也算一个;
思路:每个数字ai可能很大1e9附近,所以直接用树状数组做会爆内存,那就先离散化一下。。。我跟队友CillyB学的用结构体离散化(其实忘记了以前自己怎么离散化的,觉得这个方法不错就学了过来),如果后面有一个数字,那他前面所有最后一个数字比他小的序列都可以跟他一起构成一个递增序列,比如用dp[i]表示以数字i为结尾序列的长度,举个例子 1 2 3 4 5,dp[1] = 1,dp【2】 = 1 + dp【1】,dp【3】 = 1 + dp【1】 + dp【2】,因为1,2为结尾的序列都可以跟3拼在一起,所以3的序列就是 1 + dp【1】 + dp【2】(加上它本身),这个我们可以用树状数组维护啊!比如更新3这个值,就是求前面有几个比他小的 然后把这个值更新给3这个位置,注意是把前面自序列和更新给3
。。网上有些题解说这是个dp,但是根本没有“决策”的过程啊。。就是个递推式。。
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
using namespace std;
const int maxn = 1e5 + 5;
const int MOD = 1000000007;
int li[maxn], c[maxn];
struct node
{
int id, v;
}a[maxn];
int lowbit(int k)
{
return k & (-k);
}
void update(int k, int v)
{
while(k < maxn)
{
c[k] = (v + c[k])%MOD;
k += lowbit(k);
}
}
int sum(int k)
{
int sum = 0;
while(k > 0)
{
sum = (c[k] + sum)%MOD;
k -= lowbit(k);
}
return sum;
}
int cmp(node a, node b)
{
return a.v < b.v;
}
int main()
{
// freopen("123.txt", "w", stdout);
int t, n, Case = 0;
long long ans;
scanf("%d", &t);
while(t--)
{
memset(c, 0, sizeof(c));
memset(li, 0, sizeof(li));
scanf("%d", &n);
for(int i = 1; i <= n; i++)
scanf("%d", &a[i].v), a[i].id = i;
sort(a+1, a+1+n, cmp); //先排序
for(int i = 1; i <= n; i++) //这里就是离散化的代码,id就是最开始每个数字的下标,代表了这个数字的“身份”
{
li[a[i].id] = i; //把每个数字赋值1,2,3,4,达到离散化的标准
if(a[i].v == a[i-1].v) //去重
li[a[i].id] = li[a[i-1].id];
}
ans = 0;
for(int i = 1; i <= n; i++)
{
ans = (ans + sum(li[i]-1)+1)% MOD; //这里要查询对前面和-1之前的,因为可能有重复的,这样会多+,比如1,1,2,2
update(li[i], sum(li[i]-1)+1);
}
printf("Case %d: %lld\n", ++Case, ans);
}
return 0;
}