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

[HNOI2017]影魔

小結 eve == sca 端點 部分 自然 要求 names

標簽:線段樹

題解:

  首先對於題目的條件進行分析,p1的條件是一個區間的兩個端點必須是這一個區間的最大與次大值。p2的條件是一個區間的一個端點是最大值,而另一個端點不是次大值。顯然,他們都需要兩個條件(對於兩個端點都有要求,這樣的話不太好操作)。感受一下,p1的條件更為嚴苛,於是我們這樣考慮:
  一個區間的一個端點是最大值(兩個條件的公共部分),另一個不管,此時加上p2的貢獻。
  我們可以通過線段樹求解出這一個東西。對於i,找到右邊大於他的第一個數,如果沒有,自然是最後一個數,但是這樣不好,於是我們加一個第n+1個數為+∞。然後設ri為右邊大於他的第一個數,那麽[i,i+1]、[i,i+2]、......[i,ri]都符合條件,於是在線段樹中對於[i+1,ri]都+=p2。對於全部詢問排序,給以i為左端點的區間加上貢獻,查詢[i+1,R]的和。反過來再搞一遍,註意詢問區間也要反過來。

  然後我們發現題目中的對另一個端點的限制是互補的,我們的區間[i,ri]滿足p1,而[i,i+1~ri-1]滿足p2。因為只有一個滿足p1,而且一個i對應一個ri,於是我們在ri處+p1-p2-p2即可,完美解決這一個問題。
小結:此題關鍵在於兩個條件有一半相同,而另一半互補,所以可以不管互補的那一半到時候再考慮。

  1 #include<cstdio>
  2 #include<cstring>
  3 #include<iostream>
  4 #include<algorithm>
  5 #define ls k*2
  6
#define rs (k*2+1) 7 #define LL long long 8 using namespace std; 9 const int MAXN=210000; 10 int n,m,p1,p2,tp; 11 int v[MAXN],aft[MAXN],st[MAXN]; 12 LL ans[MAXN],sum[MAXN*5],lz[MAXN*5]; 13 struct ed 14 { 15 int L,R,id; 16 }ask[MAXN]; 17 #define down() 18 { 19 sum[ls]+=lz[k]*(mid-ll+1
); 20 sum[rs]+=lz[k]*(rr-mid); 21 lz[ls]+=lz[k]; 22 lz[rs]+=lz[k]; 23 lz[k]=0; 24 } 25 inline int gi() {int res; scanf("%d",&res); return res;} 26 bool comp(ed x,ed y){return x.L<y.L;} 27 int find(int x) 28 { 29 int L=1,R=m,mid; 30 while(L<=R) 31 { 32 mid=(L+R)/2; 33 if(ask[mid].L<x) 34 L=mid+1; 35 else 36 R=mid-1; 37 } 38 return L; 39 } 40 void add(int k,int ll,int rr,int L,int R,LL Val) 41 { 42 if(ll==L && rr==R) 43 { 44 sum[k]+=Val*(rr-ll+1); 45 lz[k]+=Val; 46 return; 47 } 48 int mid=(ll+rr)/2; 49 down(); 50 if(R<=mid) 51 add(ls,ll,mid,L,R,Val); 52 else if(mid<L) 53 add(rs,mid+1,rr,L,R,Val); 54 else 55 { 56 add(ls,ll,mid,L,mid,Val); 57 add(rs,mid+1,rr,mid+1,R,Val); 58 } 59 sum[k]=sum[ls]+sum[rs]; 60 } 61 LL query(int k,int ll,int rr,int L,int R) 62 { 63 if(ll==L && rr==R) return sum[k]; 64 int mid=(ll+rr)/2; 65 down(); 66 if(R<=mid) 67 return query(ls,ll,mid,L,R); 68 else if(mid<L) 69 return query(rs,mid+1,rr,L,R); 70 else 71 return query(ls,ll,mid,L,mid)+query(rs,mid+1,rr,mid+1,R); 72 } 73 void work() 74 { 75 tp=0; st[0]=n+1; 76 for(int i=n;i>=1;i--) 77 { 78 while(tp && v[st[tp]]<v[i]) tp--; 79 aft[i]=st[tp]; 80 st[++tp]=i; 81 } 82 memset(sum,0,sizeof sum); 83 memset(lz,0,sizeof lz); 84 for(int i=n;i>=1;i--) 85 { 86 if(aft[i]>i) 87 { 88 add(1,1,n+1,i+1,aft[i],p2); 89 add(1,1,n+1,aft[i],aft[i],p1-2*p2); 90 } 91 int p=find(i); 92 while(ask[p].L==i) 93 { 94 ans[ask[p].id]+=query(1,1,n+1,i+1,ask[p].R); 95 p++; 96 } 97 } 98 } 99 int main() 100 { 101 n=gi();m=gi();p1=gi();p2=gi(); 102 for(int i=1;i<=n;i++) v[i]=gi(); 103 for(int i=1;i<=m;i++) ask[i].L=gi() , ask[i].R=gi() , ask[i].id=i; 104 sort(ask+1,ask+1+m,comp); 105 work(); 106 reverse(v+1,v+1+n); 107 for(int i=1;i<=m;i++) 108 { 109 swap(ask[i].L,ask[i].R); 110 ask[i].L=n-ask[i].L+1; 111 ask[i].R=n-ask[i].R+1; 112 } 113 sort(ask+1,ask+1+m,comp); 114 work(); 115 for(int i=1;i<=m;i++) 116 printf("%lld\n",ans[i]); 117 return 0; 118 }

[HNOI2017]影魔