版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
题目描述:
T2 序列
题目分析:
首先对这个题进行一个比较深入地思考(这一部分请读者自行完成)。
然后你会发现这道题本质上就是拉出一个“连续上升子序列”。
我们让
指向右边第一个比它大的数
,稍作思考可以发现它构成了这样一个图:
这是一个树的结构(森林),并且不会有这样的情况:
对于第一问,只需要从
处开始向右跳,一直到
,中间经过的节点数就是不能取的点数。
对于第二问,树中是祖先关系而不是父子关系的节点不能同时取,可以预处理每一棵树的答案和深度之和,然后倍增求解:
Code:
#include<bits/stdc++.h>
#define maxn 100005
#define LL long long
using namespace std;
const int Log = 16;
int n,m,a[maxn],q[maxn],f[maxn][Log+1],d[maxn],siz[maxn],lb[maxn][Log+1];
LL sum[maxn][Log+1],ds[maxn][Log+1];
LL calc(int l,int r,LL s[maxn][Log+1],int &stp){
LL ret=0;
for(int i=Log;i>=0;i--)
if(lb[r][i]>=l-1) ret+=s[r][i],r=lb[r][i];
stp=r;
return ret;
}
int main()
{
scanf("%d%d",&n,&m);
/*for(int i=1;i<=n;i++) scanf("%d",&a[i]);
for(int i=n,t=0;i>=1;i--){
while(t&&a[i]>a[q[t]]) t--;
d[i]=d[f[i][0]=q[t]]+1,q[++t]=i;
}*/
for(int i=1,t=0;i<=n;i++){
scanf("%d",&a[i]);
while(t&&a[q[t]]<a[i]) f[q[t]][0]=i,t--;
q[++t]=i;
}
for(int i=n;i>=1;i--) d[i]=d[f[i][0]]+1;
for(int i=1;i<=n;i++){
int fa=f[i][0];
siz[i]++,lb[i][0]=i-siz[i];
ds[i][0]+=d[i],sum[i][0]=ds[i][0]-1ll*d[i]*siz[i]-(siz[i]-1);
if(fa) siz[fa]+=siz[i],ds[fa][0]+=ds[i][0];
}
for(int j=1;j<=Log;j++)
for(int i=1;i<=n;i++)
f[i][j]=f[f[i][j-1]][j-1],
lb[i][j]=lb[lb[i][j-1]][j-1],
ds[i][j]=ds[i][j-1]+ds[lb[i][j-1]][j-1],//garantee ds[0][x]==0 !!
sum[i][j]=sum[i][j-1]+sum[lb[i][j-1]][j-1];
int op,l1,r1,l2,r2; LL ans;
while(m--){
scanf("%d%d%d%d%d",&op,&l1,&r1,&l2,&r2);
if(op==1){
ans=l2-l1+r1-r2;
for(int i=Log,x=r2;i>=0;i--) if(f[x][i]&&f[x][i]<=r1) ans-=1<<i,x=f[x][i];
}
else{
ans=1ll*(r2-l2)*(r2-l2+1)/2; int stp;
ans-=calc(l2,r2,sum,stp);
if(stp!=l2-1){
ans-=sum[stp][0];
ans+=calc(lb[stp][0]+1,l2-1,ds,op)-1ll*(d[stp]+1)*(l2-1-lb[stp][0]);
}
}
printf("%lld\n",ans);
}
}