【POJ】3378 Crazy Thairs(树状数组+dp+高精)
题目
传送门:QWQ
分析
题意:给个数列,求有多少五元上升组
考虑简化一下问题:如果题目求二元上升组怎么做。
仿照一下逆序对,用树状数组维护一下就ok了。
三元怎么做呢?
把二元的拓展一位就可以了,即把第三个也扔进树状数组
所以这题就渐渐明朗了:
用$ dp[i][x] $表示以$ A[x] $结尾的$ x $元上升组有多少个
那么:
$ dp[i][x]=\sum_{j=1}^{i-1} dp[j][x-1] (A[j]<A[i]) $
其中 $ dp[i][1]=1 $
因为多了一位大的就加了一位嘛
但这个看起来是$ O(n^2) $的,肯定要凉,所以扔进树状数组优化一下。
对了,这题还要离散化和高精度
代码
#include <cstdio> #include <algorithm> #include <cstring> using namespace std; const int maxn=50010,N = 50010, Base = 10000000; typedef long long LL; class BigNum { public: int num[7], len; BigNum():len(0) {} BigNum(int n):len(0) { for( ; n > 0; n /= Base) num[len++] = n%Base; } BigNum Bigvalueof(LL n) { len = 0; while(n) { num[len++] = n%Base; n /= Base; } return *this; } BigNum operator + (const BigNum& b) { BigNum c; int i, carry = 0; for(i = 0; i < this->len || i < b.len || carry > 0; ++i) { if(i < this->len) carry += this->num[i]; if(i < b.len) carry += b.num[i]; c.num[i] = carry%Base; carry /= Base; } c.len = i; return c; } BigNum operator += (const BigNum& b) { *this = *this + b; return *this; } void Print() { if(len == 0) {puts("0"); return ;} printf("%d", num[len - 1]); for(int i = len - 2; i >= 0; --i) for(int j = Base/10; j > 0; j /= 10) printf("%d", num[i]/j%10); puts(""); } }; typedef BigNum bign; int n; bign sum[maxn][8]; struct Node{ int v,pos; bool operator < (const Node& a) const{ return v<a.v; } }a[maxn]; void add(int x,int e,bign a){for(;x<=n;x+=x&-x)sum[x][e]+=a;} bign summ(int x,int e){bign ans;for(;x>0;x-=x&-x)ans+=sum[x][e];return ans;} int main(){ while(~scanf("%d",&n)){ for(int i=1;i<=n;i++){scanf("%d",&a[i].v);a[i].pos=i;} sort(a+1,a+1+n); memset(sum,0,sizeof(sum)); int cnt=0; bign ans=0; for(int i=1;i<=n;i++){ add(a[i].pos,1,1); for(int j=2;j<=5;j++){ add(a[i].pos,j,summ(a[i].pos-1,j-1)); } } summ(n,5).Print(); } return 0; }