[AH2017/HNOI2017]影魔
阿新 • • 發佈:2020-11-05
題目
做法
做法1(口胡)
這個做法估計會T
考慮每個數字連向後面第一個比他大的樹,對一個區間建一顆樹,對於\(i<j<k,a_i<a_j<a_k\)的詢問,即為樹上每個點\(dep-2\)之和,然後其餘類似,用莫隊維護,時間複雜度:\(O(n\sqrt{n})\),常數和碼量都巨大,於是沒有打,膜了題解。
當然,至於為什麼為\(dep-2\)之和,因為我們預設\(i<j<k,a_i<a_j<a_k\)是由\((i,k)\)區間中最大的數字來找到這個區間。
做法2
這個題可以採取離線處理的方式.先處理出每個點i左邊第一個比它大的點L[i],和右邊第一個比它大的點R[i].
那麼對於區間L[i]到R[i]有p1的貢獻.①
對於左端點在L[i]+1到i-1,右端點為R[i]的區間有p2的貢獻.②
對於左端點為L[i],右端點為i+1到R[i]-1的區間也有p2的貢獻.③
所以我們離線排序處理好.
對於①情況,我們在掃到R[i]時,更新點L[i]的貢獻
對於②情況,我們在掃到R[i]時,更新區間L[i]+1到i-1的貢獻
對於③情況,我們在掃到L[i]時,更新區間i+1到R[i]-1的貢獻
我們對於每個詢問[l,r],在掃到l-1時,我們記錄此時區間l到r的每個點的貢獻和為sum1,然後當我們掃到r的時候,再次記錄此時的區間l到r的每個點的貢獻和為sum2,顯然答案就是sum2-sum1了.
好,至於為什麼嗎,我來解釋一下,為什麼\(p1\)的貢獻只用\(L[i],R[i]\)來統計呢?(當然,這也可以證明\(p1\)區間個數實在\(O(n)\)級別的)
- 為什麼每個區間必定會被統計且只會被統計一次:首先,考慮這個\((l,r)\)是個合法的\(p1\)區間,那麼對於\((l,r)\)範圍中最大的數字\(mid\),其\(L[i]=l,R[i]=r\),且對於這個區間其他數字,絕對不可能\(L[i]=l,R[i]=r\),因為\(mid\)比他們都打,卡在中間擋住了他們。
- 為什麼\((L[i],R[i])\)一定構成一個合法區間:這不廢話?
對於\(p2\)的\(a_i<a_j<a_k\)
當然,別忘了\((i,i+1)\)固定有\(p1\)的貢獻。
時間複雜度:\(O(nlogn)\)
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
#define N 210000
#define NN 410000
#define NNN 610000
using namespace std;
typedef long long LL;
struct node
{
int l,r,d;
LL lazy,c;
}tr[NN];int len,last[N];
inline void pushlazy(int x,LL k){tr[x].lazy+=k;tr[x].c+=k*tr[x].d;}
inline void updata(int x){tr[x].c=tr[tr[x].l].c+tr[tr[x].r].c;}
inline void downdata(int x)
{
if(tr[x].lazy)
{
pushlazy(tr[x].l,tr[x].lazy);
pushlazy(tr[x].r,tr[x].lazy);
tr[x].lazy=0;
}
}
void bt(int l,int r)
{
int now=++len;tr[now].d=(r-l+1);
if(l<r)
{
int mid=(l+r)>>1;
tr[now].l=len+1;bt(l,mid);
tr[now].r=len+1;bt(mid+1,r);
}
}
void change(int now,int l,int r,int ll,int rr,LL k)
{
if(l==ll && r==rr){pushlazy(now,k);return ;}
int mid=(l+r)>>1;
downdata(now);
if(rr<=mid)change(tr[now].l,l,mid,ll,rr,k);
else if(mid<ll)change(tr[now].r,mid+1,r,ll,rr,k);
else change(tr[now].l,l,mid,ll,mid,k),change(tr[now].r,mid+1,r,mid+1,rr,k);
updata(now);
}
LL findans(int now,int l,int r,int ll,int rr)
{
if(l==ll && r==rr)return tr[now].c;
int mid=(l+r)>>1;
downdata(now);
if(rr<=mid)return findans(tr[now].l,l,mid,ll,rr);
else if(mid<ll)return findans(tr[now].r,mid+1,r,ll,rr);
else return findans(tr[now].l,l,mid,ll,mid)+findans(tr[now].r,mid+1,r,mid+1,rr);
}
int L[N],R[N],n,m,a[N];
LL q1,q2;
int sta[N],top;
struct Query
{
int l,r;
LL *id,type;
};
struct CHANGE
{
int l,r,k,id/*在訪問到哪個人時會使用這個*/;
}ch[NNN];int clen,cnow=1;
inline bool cmp(CHANGE x,CHANGE y){return x.id<y.id;}
vector<Query> fuck[N];
LL ans[N];
inline void solve(int x/*加入第x個位置*/)
{
while(cnow<=clen && ch[cnow].id==x)
{
if(ch[cnow].l<=ch[cnow].r)change(1,1,n,ch[cnow].l,ch[cnow].r,ch[cnow].k);
cnow++;
}
for(int i=0;i<fuck[x].size();i++)
{
Query y=fuck[x][i];
(*y.id)+=y.type*findans(1,1,n,y.l,y.r);
}
}
int main()
{
scanf("%d%d%d%d",&n,&m,&q1,&q2);
for(int i=1;i<=n;i++)scanf("%d",&a[i]);
for(int i=1;i<=n;i++)
{
while(top && a[sta[top]]<a[i])top--;
L[i]=sta[top];
sta[++top]=i;
}
top=0;
for(int i=n;i>=1;i--)
{
while(top && a[sta[top]]<a[i])top--;
R[i]=sta[top];
sta[++top]=i;
}
for(int i=1;i<=n;i++)
{
if(L[i] && R[i])
{
ch[++clen].id=R[i];ch[clen].k=q1;ch[clen].l=L[i];ch[clen].r=L[i];
}
if(R[i])
{
ch[++clen].id=R[i];ch[clen].k=q2;ch[clen].l=L[i]+1;ch[clen].r=i-1;
}
if(L[i])
{
ch[++clen].id=L[i];ch[clen].k=q2;ch[clen].l=i+1;ch[clen].r=!R[i]?n:R[i]-1;
}
}
bt(1,n);
sort(ch+1,ch+clen+1,cmp);
for(int i=1;i<=m;i++)
{
int l,r;scanf("%d%d",&l,&r);
ans[i]=(r-l)*q1;
Query x;x.l=l;x.r=r;x.type=-1;x.id=&ans[i];
fuck[l-1].push_back(x);
x.type=1;x.id=&ans[i];
fuck[r].push_back(x);
}
for(int i=1;i<=n;i++)
{
solve(i);
}
for(int i=1;i<=m;i++)printf("%lld\n",ans[i]);
return 0;
}