1. 程式人生 > >[bzoj4826][HNOI2017]影魔

[bzoj4826][HNOI2017]影魔

題目描述

影魔,奈文摩爾,據說有著一個詩人的靈魂。事實上,他吞噬的詩人靈魂早已成千上萬。千百年來,他收集了各式各樣
的靈魂,包括詩人、牧師、帝王、乞丐、奴隸、罪人,當然,還有英雄。每一個靈魂,都有著自己的戰鬥力,而影魔,靠
這些戰鬥力提升自己的攻擊。奈文摩爾有 n 個靈魂,他們在影魔寬廣的體內可以排成一排,從左至右標號 1 到 n。
第 i個靈魂的戰鬥力為 k[i],靈魂們以點對的形式為影魔提供攻擊力,對於靈魂對 i,j(i

做法

來看看到底要統計什麼?對於(i,j)滿足i<j如果
max(a[i+1~j-1])<min(a[i],a[j])
這樣有t1個,貢獻就是t1*p1。
如果
min(a[i],a[j])<

max(a[i+1~j-1])<max(a[i],a[j])
這樣有t2個,貢獻就是t2*p2。
這個t2不是很好算,考慮轉化成
max(a[i+1~j-1]<max(a[i],a[j])
然後減掉第一種情況
考慮對每個點i,求出left[i]和right[i]表示往左和往右第一個大於自己的位置。
對於第一種,列舉一個i作為較小的位置,較大的位置j只能是left[i]或right[i]。
對於第二種,列舉一個i作為較大的位置,較小的位置j只能夾在left[i]和i或i和right[i]之間。
第一種的式子ri=l[left[i]>=l]+[right[i]<=
r]

第二種的式子
ri=l(imax(left[i],l1)1)+(min(right[i],r+1)i1)
這個計算,拿個主席樹。

#include<cstdio>
#include<algorithm>
#define fo(i,a,b) for(i=a;i<=b;i++)
#define fd(i,a,b) for(i=a;i>=b;i--)
using namespace std;
typedef long long ll;
const int maxn=200000+10,maxtot=5000000+10;
int a[maxn],left[maxn],right[maxn],ask[maxn][2
],id[maxn]; int root[maxn],size[maxtot],tree[maxtot][2],sta[80]; ll num[maxtot],sum[maxtot]; ll cnt[maxn][2],ans,x,ans1,ans2,ans3; int i,j,k,l,r,t,n,m,p1,p2,top,tot; int read(){ int x=0,f=1; char ch=getchar(); while (ch<'0'||ch>'9'){ if (ch=='-') f=-1; ch=getchar(); } while (ch>='0'&&ch<='9'){ x=x*10+ch-'0'; ch=getchar(); } return x*f; } bool cmp1(int x,int y){ return left[x]>left[y]; } bool cmp2(int x,int y){ return right[x]<right[y]; } int newnode(int x){ ++tot; tree[tot][0]=tree[x][0]; tree[tot][1]=tree[x][1]; size[tot]=size[x]; num[tot]=num[x]; sum[tot]=sum[x]; return tot; } void insert(int &x,int l,int r,int a,int p){ x=newnode(x); if (l==r){ size[x]++; num[x]+=(ll)l; sum[x]+=p?(ll)right[l]:(ll)left[l]; return; } int mid=(l+r)/2; if (a<=mid) insert(tree[x][0],l,mid,a,p);else insert(tree[x][1],mid+1,r,a,p); size[x]=size[tree[x][0]]+size[tree[x][1]]; num[x]=num[tree[x][0]]+num[tree[x][1]]; sum[x]=sum[tree[x][0]]+sum[tree[x][1]]; } void query(int x,int l,int r,int a,int b){ if (!x) return; if (l==a&&r==b){ ans1+=size[x]; ans2+=num[x]; ans3+=sum[x]; return; } int mid=(l+r)/2; if (b<=mid) query(tree[x][0],l,mid,a,b); else if (a>mid) query(tree[x][1],mid+1,r,a,b); else{ query(tree[x][0],l,mid,a,mid); query(tree[x][1],mid+1,r,mid+1,b); } } ll getsum(int n){ return (ll)n*(n+1)/2; } void write(ll x){ if (!x){ putchar('0'); putchar('\n'); return; } top=0; while (x){ sta[++top]=x%10; x/=10; } while (top) putchar('0'+sta[top--]); putchar('\n'); } int main(){ freopen("sf.in","r",stdin);freopen("sf.out","w",stdout); n=read();m=read();p1=read();p2=read(); fo(i,1,n) a[i]=read(); fo(i,1,m) ask[i][0]=read(),ask[i][1]=read(); fo(i,1,n){ j=i-1; while (j&&a[j]<a[i]) j=left[j]; left[i]=j; } fd(i,n,1){ j=i+1; while (j<=n&&a[j]<a[i]) j=right[j]; right[i]=j; } fo(i,1,n) id[i]=i; sort(id+1,id+n+1,cmp1); tot=0; j=1; fd(i,n,1){ root[i]=root[i+1]; k=j-1; while (k<n&&left[id[k+1]]==i) k++; fo(t,j,k) insert(root[i],1,n,id[t],0); j=k+1; } fo(i,1,m){ l=ask[i][0];r=ask[i][1]; ans1=ans2=ans3=0; query(root[l],1,n,l,r); cnt[i][0]+=ans1; cnt[i][1]+=ans2-ans3-ans1; ans1=(r-l+1)-ans1; ans2=getsum(r)-getsum(l-1)-ans2; cnt[i][1]+=ans2-(ll)ans1*l; } fo(i,1,tot) tree[i][0]=tree[i][1]=size[i]=sum[i]=num[i]=0; tot=0; fo(i,1,n) id[i]=i; sort(id+1,id+n+1,cmp2); tot=0; j=1; fo(i,1,n){ root[i]=root[i-1]; k=j-1; while (k<n&&right[id[k+1]]==i) k++; fo(t,j,k) insert(root[i],1,n,id[t],1); j=k+1; } fo(i,1,m){ l=ask[i][0];r=ask[i][1]; ans1=ans2=ans3=0; query(root[r],1,n,l,r); cnt[i][0]+=ans1; cnt[i][1]+=ans3-ans2-ans1; ans1=(r-l+1)-ans1; ans2=getsum(r)-getsum(l-1)-ans2; cnt[i][1]+=(ll)ans1*r-ans2; } fo(i,1,m){ l=ask[i][0];r=ask[i][1]; cnt[i][1]-=cnt[i][0]; ans=(ll)p1*cnt[i][0]; ans+=(ll)p2*cnt[i][1]; write(ans); } }