[JZOJ5073]【GDOI2017第三轮模拟day1】影魔

来源:互联网 发布:为什么淘宝没有电棒 编辑:程序博客网 时间:2024/06/11 02:35

题目描述

有一个长度为n的排列a[],定义一个点对(i,j) (i < j)的贡献:

  1. max(a[i+1 j1])<min(a[i],a[j]),贡献p1
  2. sort(a[i],a[j],max(a[i+1 j1]))后,max(a[i+1 j1])为第二项,贡献p2
    给m个询问,每次询问一个区间[l,r]的贡献和。
    30%:1<= n,m <= 500。
    另30%: p1=2*p2。
    100%:1 <= n,m <= 200000;1 <= p1,p2 <= 1000。

分析

我们可以先从第二档分析一下。
p1=2*p2提示我们拆开p1,把情况1转化为2,怎么弄呢?
先看看情况2吧。考虑i作为左端点(右端点相似),对于一个点对(i,j),如果这max(a[i+1 j1])小于a[i],而且而且a[j]小于max(a[i+1 j1]),则贡献。那么如果把后面那个条件去掉,在考虑i做完两个端点后,某些点对(i,j)被算了两次,而被算重的点对,恰好就是能贡献p1的。
那么做法出来了:
对于i作为左端点。
1. 用单调栈弄出每一个点i向右跳的第一个比他大的点,记为next[i];
2. 离线挂询问从右到左扫,把点i为左端点的贡献记在右端点上,即线段树给[i+1,next[i]]打上+p2标记。
3. 对于左端点在i的询问(l,r),查询[l,r]的权值和。
4. i为右端点的话就翻转所有下标再做一次就好了。

那么满分怎么做呢?考虑到(i,next[i])构成了贡献p1的点对,而又被加了两次的p2,那么用刚刚的算法,每次做到i,就给贡献数组的next[i]那一位加上p1-2*p2,注意到同一点对p1不会加两次。

代码

#include<cstdio>#include<algorithm>#include<cstring>#include<cmath>#include<map>using namespace std;typedef long long ll;typedef double db;#define fo(i,j,k) for(i=j;i<=k;i++)#define fd(i,j,k) for(i=j;i>=k;i--)const int N=200010;struct rec{    int l,r,id;}que[N];struct seg{    ll sum,tag;}tr[N*4];int n,m,p1,p2,a[N],k,l[N],r[N],next[N],sta[N],st,le,i,j;ll prt[N];bool cmp(rec a,rec b){    return a.l>b.l;}void down(int x,int l,int r){    int m=(l+r)/2;    tr[x].sum+=tr[x].tag*((ll)r-l+1);    if (l!=r)    {        tr[x*2].tag+=tr[x].tag;        tr[x*2+1].tag+=tr[x].tag;    }    tr[x].tag=0;}void change(int x,int l,int r,int i,int j,int val){    if (i>j) return;    int m=(l+r)/2;    down(x,l,r);    if (l!=r)    {        down(x*2,l,m);        down(x*2+1,m+1,r);    }    if (l==i&&r==j)    {        tr[x].tag+=val;        down(x,l,r);        return;    }    if (j<=m) change(x*2,l,m,i,j,val);else    if (m<i) change(x*2+1,m+1,r,i,j,val);else    {        change(x*2,l,m,i,m,val);        change(x*2+1,m+1,r,m+1,j,val);    }    tr[x].sum=tr[x*2].sum+tr[x*2+1].sum;}ll get(int x,int l,int r,int i,int j){    if (i>j) return 0;    int m=(l+r)/2;    down(x,l,r);    if (l!=r)    {        down(x*2,l,m);        down(x*2+1,m+1,r);    }    if (l==i&&r==j) return tr[x].sum;    if (j<=m) return get(x*2,l,m,i,j);else    if (m<i) return get(x*2+1,m+1,r,i,j);else        return get(x*2,l,m,i,m)+get(x*2+1,m+1,r,m+1,j);    tr[x].sum=tr[x*2].sum+tr[x*2+1].sum;    }void solve(){    fo(i,1,m) que[i].l=l[i],que[i].r=r[i],que[i].id=i;    sort(que+1,que+1+m,cmp);    a[n+1]=1e9;    sta[(st=1)]=n+1;    fd(i,n,1)    {        while (st&&a[sta[st]]<a[i]) st--;        next[i]=sta[st];        sta[++st]=i;    }    le=1;    fd(i,n,1)    {        change(1,1,n+1,i+1,next[i],+p2);        change(1,1,n+1,next[i],next[i],p1-2*p2);        while (le<=m&&que[le].l==i)        {            prt[que[le].id]+=get(1,1,n+1,i,que[le].r);            le++;        }        if (le>m) break;    }}int main(){    freopen("sf.in","r",stdin);//  freopen("sf.out","w",stdout);    scanf("%d %d %d %d",&n,&m,&p1,&p2);    fo(i,1,n) scanf("%d",a+i);    fo(i,1,m) scanf("%d %d",l+i,r+i);    solve();    fo(i,1,(n+1)*4) tr[i].sum=tr[i].tag=0;    fo(i,1,n/2) swap(a[i],a[n-i+1]);    fo(i,1,m)     {        l[i]=n-l[i]+1,r[i]=n-r[i]+1;        swap(l[i],r[i]);    }    solve();    fo(i,1,m) printf("%lld\n",prt[i]);}
0 0
原创粉丝点击