poj1990&&uva1428 树状数组

求递增五元组的个数

不断更新的动态规划?

记c[i][j]为前k个数(当前状态)中以j结尾的递增i元组的个数

若第k+1个数为a[k+1],则c[i][a[k+1]]+=sum(c[i-1][j]),2<=i<=5,1<=j<a[k+1]

答案为sum(c[5][a[i]]),每次在更新第i个数之前进行叠加!

区间和加法可以用树状数组优化!

记c[i][j]为前k个数(当前状态)中树状数组意义下以j项节点区间所有点结尾的递增i元组的个数和

进行sum和update操作即可得出结果

技巧:直接记录c[5][a[k]]=sum(4,a[k]-1),省去c[5][j]层数组,节省空间

注意大整数运算!


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


求递增三元组的个数

简化动规解法

cc[i]:从前往后扫描,当前比第i个数小的个数

dd[i]:从后往前扫描,当前比第i个数小的个数

出现第i个数,在数轴上a[i]处做标记,询问时求区间和即可。


#include<cstdio>
#include<vector>
#define lowbit(x) x&(-x)
#define ll long long
using namespace std;

struct FenwickTree{
    int n;
    vector<int> c;
    void clear() { fill(c.begin(),c.end(),0); }
    void resize(int n) { this->n=n; c.resize(n); }
    //a[1]+a[2]+...+a[x]
    int sum(int x){
        int ret=0;
        for(;x>0;x-=lowbit(x)) ret+=c[x];
        return ret;
    }
    //a[x]+=d
    void add(int x,int d) { for(;x<=n;x+=lowbit(x)) c[x]+=d; }
};

const int maxn=20000+5;
int n,a[maxn],cc[maxn],dd[maxn];
FenwickTree f;

int main(){
    int T;
    //freopen("a.txt","r",stdin);
    scanf("%d",&T);
    while(T--){
        scanf("%d",&n);
        int maxa=0;
        for(int i=1;i<=n;i++) { scanf("%d",&a[i]);maxa=max(maxa,a[i]); }
        f.resize(maxa);
        f.clear();
        for(int i=n;i>=1;i--)
            f.add(a[i],1),dd[i]=f.sum(a[i]-1);
        f.clear();
        for(int i=1;i<=n;i++)
            f.add(a[i],1),cc[i]=f.sum(a[i]-1);
        ll ans=0;
        for(int i=1;i<=n;i++)
            ans+=(ll)cc[i]*(n-i-dd[i])+(ll)(i-cc[i]-1)*dd[i];
        printf("%lld\n",ans);
    }
    return 0;
}



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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值