CF1827B2
非常好的转换
想了很久,想到了要对缝进行讨论,算每个缝的贡献,但是还是没想到最后一步。
-
可以发现,对长为一的区间排序居然不需要代价
-
手搓下对\(x\sim y\)排序与对\(x\sim z\),\(z+1\sim y\)排序,前者代价为\(y-x\),后者为\(z-x+y-z-1=y-x-1\),由此可得结论,选若干个区间排序相当于把原区间打散成小区间,且打散到单个数是相当于没选这个数,所以可以保证正确性。
-
原问题可转化为对每个子区间里的断点,即\(z\sim z+1\)被打断的这一连系,进行求和,最终在总答案中减去这些位置。(CF原文:原问题等价于求\((l,k,r)\)满足\(max(l\sim k)<min(k+1\sim r)\)的三元组个数)
从这里开始没想到的...
-
此时我们设\((l,k,r)\)中,\(min(k+1\sim r)=a_i\),则我们枚举\(i\),为了使\(max(l\sim k)<min(k+1\sim r)\),必定有\(a_k\)是\(a_i\)左边第一个小于\(a_i\)的数,所以我们可以求出\(k\),没有这样的\(k\)则这个\(i\)贡献为\(0\)。
-
接着我们可以看\(l\)所满足的限制。设\(a_x\)为\(a_k\)左边第一个大于\(a_i\)的数(若没有则\(x=0\)),\(a_y\)为\(a_i\)右边第一个小于\(a_i\)的数(若没有则\(y=n+1\)),我们可以知道,\(i\)此时对所有\(x<l<=k<i<=r<y\)的\((l,r)\)有一的贡献,所以\(i\)的贡献为\((k-x)*(y-i)\)
使用\(st\)表预处理,二分查找,时间复杂度\(O(nlogn)\)
code
#include<cstdio>
#include<algorithm>
#include<iostream>
#define int long long
using namespace std;
const int mn=3e5+5;
int n,a[mn],st[2][mn][20];
int lt[mn],ans;
void init()
{
lt[1]=0;
for(int i=2;i<=3e5;i++)
{
lt[i]=lt[i>>1]+1;
}
}
void build()
{
for(int i=1;i<=n;i++)
{
st[0][i][0]=st[1][i][0]=a[i];
}
for(int i=1;i<20;i++)
{
for(int j=1;j<=n-(1<<i)+1;j++)
{
st[0][j][i]=max(st[0][j][i-1],st[0][j+(1<<(i-1))][i-1]);
st[1][j][i]=min(st[1][j][i-1],st[1][j+(1<<(i-1))][i-1]);
}
}
}
int qry(int x,int l,int r)
{
int y=lt[r-l+1];
if(x==0)return max(st[x][l][y],st[x][r-(1<<y)+1][y]);
return min(st[x][l][y],st[x][r-(1<<y)+1][y]);
}
void solve()
{
ans=0;
scanf("%lld",&n);
for(int i=1;i<=n;i++)
{
scanf("%lld",&a[i]);
ans+=(n-i+1)*(n-i)/2;
}
build();
// cerr<<ans<<"-----\n";
// int abc=0;
for(int i=1;i<=n;i++)
{
int x=0,y=n+1,k=-1;
int l=1,r=i-1;
while(l<=r)
{
int mid=(l+r)>>1;
if(qry(1,mid,i-1)<a[i])
{
k=mid;
l=mid+1;
}
else
{
r=mid-1;
}
}
if(k==-1)continue;
l=1;
r=k-1;
while(l<=r)
{
int mid=(l+r)>>1;
if(qry(0,mid,k-1)>a[i])
{
x=mid;
l=mid+1;
}
else
{
r=mid-1;
}
}
l=i+1;
r=n;
while(l<=r)
{
int mid=(l+r)>>1;
if(qry(1,i+1,mid)<a[i])
{
y=mid;
r=mid-1;
}
else
{
l=mid+1;
}
}
// cerr<<ans<<" "<<i<<" "<<x<<" "<<y<<" "<<k<<'\n';
ans-=(k-x)*(y-i);
}
// abc/=2;
printf("%lld\n",ans);
}
signed main()
{
init();
int T;
scanf("%lld",&T);
while(T--)
{
solve();
}
}

浙公网安备 33010602011771号