poj3378 Crazy Thairs

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


 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值