真的比较神的一个题
Description
给定一个排列,如果点对 ((i,j)) 满足:
(1.) (i ightarrow j) 中(两端不含)元素均小于 (p_i) 和 (p_j) 则产生 (a_1) 点贡献
(2.) (i ightarrow j)中(两端不含)元素的最大值如果在(p_i) 和 (p_j) 之间则产生 (a_2) 点贡献
Solution
首先发现要贡献法……想不到就没救啦
分开考虑两种值
第一种我们枚举一个点当做区间最大值,找到左右离得最近的比它大的两个数,这里就产生了贡献
然后第二种
对于每个点(i)
(l ightarrow i) 和 (r)
(i ightarrow r) 和 (l)
分别是满足条件的
然后我们看怎么求
左右的两个大数是可以单调栈求的
for(int i=1;i<=n+1;++i)
{
while(top&&a[st[top]]<a[i]) r[st[top]]=i,top--;
l[i]=st[top]; st[++top]=i;
}
然后我们统计答案(蒟蒻不会这里的统计答案)
其实就是个区间加区间求和的线段树,不知道为啥没想到
还搞了(std) 才看懂……
Code
#include<bits/stdc++.h>
using namespace std;
#define int long long
namespace yspm{
inline int read()
{
int res=0,f=1; char k;
while(!isdigit(k=getchar())) if(k=='-') f=-1;
while(isdigit(k)) res=res*10+k-'0',k=getchar();
return res*f;
}
const int N=2e5+10;
struct tree{
int l,r,sum,add;
#define l(p) t[p].l
#define r(p) t[p].r
#define sum(p) t[p].sum
#define add(p) t[p].add
}t[N<<5];
int rt[N],tot,l[N],r[N],st[N],top,n,m,p1,p2,a[N];
inline void build(int p,int l,int r)
{
l(p)=l; r(p)=r; if(l==r) return ;
int mid=(l+r)>>1; build(p<<1,l,mid); build(p<<1|1,mid+1,r);
return ;
}
inline void spread(int p)
{
if(add(p))
{
sum(p<<1)+=add(p)*(r(p<<1)-l(p<<1)+1);
sum(p<<1|1)+=add(p)*(r(p<<1|1)-l(p<<1|1)+1);
add(p<<1)+=add(p); add(p<<1|1)+=add(p);
}return add(p)=0,void();
}
inline void push_up(int p){return sum(p)=sum(p<<1)+sum(p<<1|1),void();}
inline void update(int p,int l,int r,int v)
{
if(l<=l(p)&&r(p)<=r) return sum(p)+=v*(r(p)-l(p)+1),add(p)+=v,void();
int mid=(l(p)+r(p))>>1; spread(p);
if(l<=mid) update(p<<1,l,r,v);
if(r>mid) update(p<<1|1,l,r,v);
return push_up(p);
}
inline int ask(int p,int l,int r)
{
if(l<=l(p)&&r(p)<=r) return sum(p);
spread(p); int ans=0,mid=(l(p)+r(p))>>1;
if(l<=mid) ans+=ask(p<<1,l,r); if(r>mid) ans+=ask(p<<1|1,l,r);
return ans;
}
int ans[N];
struct node{
int l,r,id,val;
}; vector<node> vec1[N],vec2[N];
signed main()
{
n=read(); m=read(); p1=read(); p2=read();
for(int i=1;i<=n;++i) a[i]=read(); a[0]=a[n+1]=n+1;
for(int i=1;i<=n+1;++i)
{
while(top&&a[st[top]]<a[i]) r[st[top]]=i,top--;
l[i]=st[top]; st[++top]=i;
}
for(int i=1;i<=m;++i)
{
int tl=read(),tr=read();
ans[i]+=(tr-tl)*p1;
vec2[tl-1].push_back((node){tl,tr,i,0});
vec2[tr].push_back((node){tl,tr,i,0});
}
for(int i=1;i<=n;++i)
{
if(l[i]&&r[i]<=n) vec1[r[i]].push_back((node){l[i],l[i],0,p1});
if(l[i]&&r[i]>i+1) vec1[l[i]].push_back((node){i+1,r[i]-1,0,p2});
if(l[i]+1<i&&r[i]<=n) vec1[r[i]].push_back((node){l[i]+1,i-1,0,p2});
}
build(1,1,n);
for(int i=1;i<=n;++i)
{
int siz=vec1[i].size();
for(int j=0;j<siz;++j)
{
update(1,vec1[i][j].l,vec1[i][j].r,vec1[i][j].val);
}
siz=vec2[i].size();
for(int j=0;j<siz;++j)
{
int sum=ask(1,vec2[i][j].l,vec2[i][j].r);
if(i==vec2[i][j].r) ans[vec2[i][j].id]+=sum;
else ans[vec2[i][j].id]-=sum;
}
}
for(int i=1;i<=m;++i) printf("%lld
",ans[i]);
return 0;
}
}
signed main(){return yspm::main();}
Review
(1.) 贡献法好!
(2.) 提升代码能力和学习如何统计答案……