题意:给定一个长度为n(n <= 100000)的整数序列,求其中的递增序列的个数。
对于某些序列中的递增序列的个数是可以用dp来求解的,其状态转移方程为:
dp[i] = sum(dp[j]) + 1,j < i &&a[j] < a[i]
根据状态转移方程可以得知这样dp的时间复杂度为O(n^2),而对于题目给定的10^6的数量级来说,这样dp是不行的。
那么我们根据a[j]<a[i],可以得知,这其实就是逆序对,而求逆序对的快速方法莫过于树状数组了。
因此我们想到了树状数组,而对于题目给的a[i]元素的大小,a[i] <= 2^31,这样的数量级,很显然直接将其应用到树状数组是不可能的。
我们发现总共的元素只有n <= 10^6 个,而a[i]的最大值可以达到2^31,所以我们为了能够应用树状数组,我们想到了离散化。
离散化:我们将a[i]从小到大排个序,并将重复的元素去掉只留下一个,那么我们可以得出,每个a[i]都能对应一个数组下标,而我们就利用这个数组下标来建立树状数组。
这样在树状数组增加值的时候我们就根据dp原理,利用树状数组的性质,完成了对后面的所有元素进行求和的运算,那么最后的出来的结果就是我们要求的结果了。
下面附上代码,上面带上解释:
#include <iostream> #include <algorithm> #include <cstdio> #include <cstring> using namespace std; #define N 100001 #define ll long long #define Lowbit(x) ((x)&(-x)) #define M 1000000007 ll C[N]; ll num[N], t[N]; int T,tot; void add(ll C[],ll pos,ll num) { while(pos <= N) {//x×î´óÊÇN C[pos] += num; if(C[pos] > M)C[pos] %= M; pos += Lowbit(pos); } } ll Sum(ll C[],ll end) { ll sum = 0; while(end > 0) { sum += C[end]; if(sum > M)sum%= M; end -= Lowbit(end); } return sum; } int binarysearch(int x){//二分查找下标 int mid, l = 1 ,r = tot; while(l <= r){ mid = (l + r)>>1; if(x == t[mid])return mid; else if(x > t[mid]){ l = mid + 1; } else r = mid - 1; } return -1; } int main() { int n, s, i, j, T, k, m; ll ans, tmp; while(~scanf("%d",&T)) { ans = 0; for(i = 0; i < T; i ++) { scanf("%I64d",&num[i]); t[i+1] = num[i]; } sort(t+1,t+1+T);//开始离散化 tot = 1; C[tot] = 0; for(i = 2; i <= T; i++){//去重 if(t[ i ] != t[i - 1]){ t[++tot] = t[i]; C[tot] = 0; } } for(i = 0; i < T; i ++){//将原来的数更改为其对应的数组下标 num[i] = binarysearch(num[i]); } ans = 0; add(C,1,1);//因为每个元素都要加一个自己,所以先将所有的元素加一 for(i = 0; i < T; i++){ m = Sum(C,num[i]);//求出其前面有多少个小于它的数, ans += m; if(ans > M)ans %= M;//加起来,取模 add(C,num[i],m);//将其值加进树状数组,原理为dp思想。 } printf("%I64d\n",ans); } return 0; }