求s序列连续定长子序列的个数(len=5)
1.状态dp[i][j]:长度为i以第j个元素结尾的定长子序列个数
2.状态转移方程:dp[i][j]=sum{dp[i-1][k],s[k]<s[j],1<=k<=j-1}
3.优化:求dp[i-1]数组前j-1项中比s[j]小的元素个数---树状数组。
树状数组的应用之一:求A数组中前k-1项中比第k项小的元素的个数(数组中元素均正)
具体做法:1.在数组中元素变换范围上建立数轴。依次进入元素A[1,2,3...N],每进入一个第i位元素,将数轴上A[i]标记为1,统计数轴上[1...A[i]-1]的标记之和
树状数组的本质作用:在O(logn)时间内求区间元素(len=n)的和,并支持元素更新操作。
4.本题还需要离散化数据。
离散化的本质:在不改变数的相对大小前提下,尽可能大地缩小数的变化范围,以节省空间和遍历的时间
#include<stdio.h>
#include<string.h>
#include<iostream>
#include<algorithm>
#define maxn 50010
#define mem(a,b) memset(a,b,sizeof(a))
#define lowbit(x) x&(-x)
#define rad 10000//大整数运算中的单位
using namespace std;
struct bint{
int v[8],l;
bint(){
v[1]=0;
l=1;
}
};
int n,s[maxn],s0[maxn],s1[maxn];
bint c[5][maxn];
/*大整数运算*/
inline void print(bint a){
printf("%d",a.v[a.l]);
for(int i=a.l-1;i;i--)
printf("%04d",a.v[i]);
printf("\n");
}
bint operator+(bint a,bint b){
if(a.l<b.l)swap(a,b);
int t=b.l;
for(int i=1;i<=t;i++)
a.v[i]+=b.v[i];
for(int i=1;i<=t;i++)
if(a.v[i]>=rad)
{
if(i==a.l)
{
a.v[i+1]=0;
a.l++;
}
a.v[i+1]+=a.v[i]/rad;
t=max(t,i+1);
a.v[i]%=rad;
}
return a;
}
inline bint operator+(bint a,int p){
bint b;
b.v[1]=p;b.l=1;
return a+b;
}
void seperate(){//离散化
sort(s0+1,s0+n+1);
for(int i=1;i<=n;i++)
s1[i]=lower_bound(s0+1,s0+n+1,s[i])-s0;//映射到1...n中去
}
void update(int i,int j,bint x){
for(;j<=n;j+=lowbit(j)) c[i][j]=c[i][j]+x;
}
bint sum(int i,int j){
bint ss;
for(;j>0;j-=lowbit(j)) ss=ss+c[i][j];
return ss;
}
int main(){
while(scanf("%d",&n)!=EOF){
for(int i=1;i<=n;i++){
scanf("%d",&s[i]);
s0[i]=s[i];
}
seperate();
mem(c,0);
bint ans;
for(int j=1;j<=n;j++){
bint tmp;
tmp.v[1]=1;//tmp是1
update(1,s1[j],tmp);
ans=ans+sum(4,s1[j]-1);//因为只需要输出sum(5,n),用ans每次计数,省去一个树状数组,节省空间和时间
for(int i=2;i<5;i++){
update(i,s1[j],sum(i-1,s1[j]-1));//sum(1,s[j]-1)
}
}
print(ans);
}
}