1. 程式人生 > >牛客訓練(BIT+高精度)

牛客訓練(BIT+高精度)

又是這類用BIT輔助計數的題。。

這個顯然滿足要求的區間比不滿足的要多太多,所以變成求不滿足的。。。

然後要先求總方案數,為

\sum_{l_2=1}^{n}\sum_{r_2=l_2}^{n}l_2(l_2+1)/2=\sum_{l_2=1}^{n}l_2(l_2+1)(n-l_2+1)/2

這個不是很想在化了,反正O(n)求也可以的。。

然後求不滿足的。。這裡已經有4個端點,如果再列舉端點什麼的顯然很不明智。。因此應該從區間的性質入手,所以要列舉最值。。

列舉[l2,r2]的max,從大到小列舉,設此時的位置為i,可以發現,能構成區間2的個數有(i-pre[i])*(next[i]-i),其中pre和next代表比a[i]大且裡i最近的左邊的數和右邊的數。。此時有發現,其實區間1已經被pre[i]分隔開了,因為pre[i]後面的數區間1是絕對不能取的(否則區間1的min會小於區間2的max),然後現在就是算區間1的個數了,可以發現區間1必須完全由之前被列舉過的數構成,那麼只要用BIT維護一下i之前的這些區間塊的個數和大小就可以了。。

這個要如何維護呢?每次列舉的時候判斷一下左右是否被列舉過,如果有直接合並,把區間1的方案數修改一下即可。。

然後pre是用BIT算的,貌似有點浪費。。

另外總方案數會爆ll,之前學杜教的小高精終於能派上用場了。。

/**
 *        ┏┓    ┏┓
 *        ┏┛┗━━━━━━━┛┗━━━┓
 *        ┃       ┃  
 *        ┃   ━    ┃
 *        ┃ >   < ┃
 *        ┃       ┃
 *        ┃... ⌒ ...  ┃
 *        ┃       ┃
 *        ┗━┓   ┏━┛
 *          ┃   ┃ Code is far away from bug with the animal protecting          
 *          ┃   ┃   神獸保佑,程式碼無bug
 *          ┃   ┃           
 *          ┃   ┃        
 *          ┃   ┃
 *          ┃   ┃           
 *          ┃   ┗━━━┓
 *          ┃       ┣┓
 *          ┃       ┏┛
 *          ┗┓┓┏━┳┓┏┛
 *           ┃┫┫ ┃┫┫
 *           ┗┻┛ ┗┻┛
 */ 
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
#include<queue>
#include<cmath>
#include<map>
#include<stack>
#include<set>
#include<bitset>
#include<stdlib.h>
#include<assert.h>
#define inc(i,l,r) for(int i=l;i<=r;i++)
#define dec(i,l,r) for(int i=l;i>=r;i--)
#define link(x) for(edge *j=h[x];j;j=j->next)
#define mem(a) memset(a,0,sizeof(a))
#define ll long long
#define eps 1e-8
#define succ(x) (1<<x)
#define lowbit(x) (x&(-x))
#define sqr(x) ((x)*(x))
#define mid (x+y)/2
#define NM 1000005 
#define nm 40005
#define pi 3.1415926535897931
const ll inf=1e16;
using namespace std;
ll read(){
    ll x=0,f=1;char ch=getchar() ;
    while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
    while(isdigit(ch))x=x*10+ch-'0',ch=getchar();
    return f*x;
}






int n,b[NM],nxt[NM],f[NM],sz[NM];
int c[NM];
ll a[NM];
__int128 ans;


void _add(int x,int t){for(;x<=n;x+=lowbit(x))c[x]=max(c[x],t);}
int _sum(int x,int s=0){for(;x;x-=lowbit(x))s=max(s,c[x]);return s;}
void add(int x,ll t){for(;x<=n;x+=lowbit(x))a[x]+=t;}
ll sum(int x,ll s=0){for(;x;x-=lowbit(x))s+=a[x];return s;}


int find(int x){return f[x]==x?x:f[x]=find(f[x]);}
ll fun(ll n){return n*(n+1)/2;}

int main(){
    n=read();inc(i,1,n)b[read()]=i;
    inc(i,1,n+1)f[i]=i;
    nxt[0]=n+1;
    inc(i,1,n)ans+=(ll)i*(i-1)/2*(n-i+1);
    dec(k,n,1){
	int i=b[k];
	int l=_sum(i);int r=nxt[l];
	nxt[l]=i;nxt[i]=r;_add(i,i);
       	__int128 s=(ll)(i-l)*(r-i);
	s*=sum(l);ans-=s;
	sz[i]=1;
	if(l==i-1){
	    int x=find(l);
	    sz[i]+=sz[x];f[x]=i;if(sz[x])add(x,-fun(sz[x]));
	}
	if(r==i+1){
	    int x=find(r);
	    sz[i]+=sz[x];f[x]=i;if(sz[x])add(x,-fun(sz[x]));
	}
	add(i,fun(sz[i]));
    }
    ll tmp;
    if(ans<inf)printf("%lld\n",tmp=ans);
    else{
	tmp=ans/inf;ll t=ans%inf;
	printf("%lld%016lld\n",tmp,t);
    }
    return 0;
}

Trophies

時間限制:C/C++ 2秒,其他語言4秒 空間限制:C/C++ 524288K,其他語言1048576K 64bit IO Format: %lld

題目描述

蒜頭的家裡有一張長長的桌子,桌子上有n個獎盃排成一排。我們用xi表示第i個獎盃的高度。獎盃的高度兩兩不同,為了方便,我們保證x是一個的排列。

由於蒜頭的桌子已經放不下新的獎盃了,他打算將一些獎盃送給修修和棟棟。具體來說,他打算選擇四個引數l1,r1,l2,r2 (1 ≤ l1 ≤ r1 < l2 ≤ r2 ≤ n),

然後將區間[l1,r1]中的獎盃送給修修,將區間[l2,r2]中的獎盃送給棟棟。 修修不希望他拿到的獎盃中最矮的一個比棟棟拿到的獎盃中最高的一個還要高,因此他想知道有多少種方案滿足

輸入描述:

第一行一個整數n (1 ≤ n ≤ 106),第二行n個整數 (1 ≤ xi ≤ n)。

輸出描述:

輸出一行一個整數表示方案數。

示例1

輸入

複製

5
1 4 3 5 2

輸出

複製

28