[NOI2020]时代的眼泪

时代的眼泪

题解

变成时代的眼泪了。

貌似是一个奇怪的区间顺序对问题。
数据范围 n ⩽ 1 0 5 n\leqslant 10^5 n105,大概是树套树或者分块,感觉分块比较可行,考虑分块。
原题相当于就是求 ∑ i < j , i , j ∈ [ x , x ′ ] [ y ⩽ p i < p j ⩽ y ′ ] \sum_{i<j,i,j\in[x,x']}[y\leqslant p_i<p_j\leqslant y'] i<j,i,j[x,x][ypi<pjy],我们就对它们的下标分块,方便维护第一维。

然后考虑之后怎么计算答案。
首先是整块内部的贡献,由于对于一个整块,不同意义的 [ x ′ , y ′ ] [x',y'] [x,y]只有不超过 B 2 B^2 B2种,我们完全可以暴力将它们都计算出来。
也就是说我们把原来的询问离散化到块内的 B B B个点上,然后可以区间 d p dp dp计算答案。
我们的离散化显然不能每次都 lower_bound \text{lower\_bound} lower_bound,所以需要对于每个块再预处理一下每个值会被离散化到哪个点上。
这部分是 O ( n B + n 2 B ) O\left(nB+\frac{n^2}{B}\right) O(nB+Bn2)的。

那么整块之间的贡献怎么计算呢?
我们先预处理出来当前块中每个点与其它块之间的答案,这可以通过归并得到,按它们的值排序做个前缀和,再按块的顺序做个前缀和。这显然是 O ( n B ) O\left(nB\right) O(nB)的。
这样,我们稍微差分一下,就能达到当前块中某个前缀关于它前面块的顺序对总个数。
我们再按之前离散化得到的值,差分就能得到当前块中在 [ y , y ′ ] [y,y'] [y,y]中的数关于前面块的顺序对数。
最后,还需要再减去当前块中 [ y , y ′ ] [y,y'] [y,y]中的数与前面块小于 y y y的数形成的顺序对。由于两者是必然形成顺序对的,之间统计前面块在 [ 1 , y ′ ) [1,y') [1,y)中有多少数即可。这需要做一个块前缀和的前缀和,同样能 O ( n 2 B ) O\left(\frac{n^2}{B}\right) O(Bn2)地解决。
这部分还是 ( n B ) \left(nB\right) (nB)的。

接下来考虑散块与整块的贡献。
诶,这可以通过我们块前缀和的前缀和差分得到。
直接在散块的位置把关于它前面或后面的顺序对算一算即可。
然后就是散块内部的贡献了,这个可以通过在块内算一下每个点的前缀顺序对数量,然后差分得到。
只有当每个点在区间 [ y , y ′ ] [y,y'] [y,y]中时,才统计它的贡献,还得减去它前面小于 y y y的数的数量。
这部分是 O ( m B ) O\left(mB\right) O(mB)的。
至于散块与散块的贡献,整块内我们是预先排好序了的,把散块的按顺序提出来归并一下就行了。
这也是 O ( m B ) O\left(mB\right) O(mB)的。

所以总时间复杂度是 O ( n 2 B + ( n + m ) B ⩾ n n + m ) O\left(\frac{n^2}{B}+(n+m)B\geqslant n\sqrt{n+m}\right) O(Bn2+(n+m)Bnn+m )

源码

写还是比较好写,但调有点难调,不过完全不卡常。不卡常的Ynoi

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
typedef unsigned long long uLL;
typedef pair<int,int> pii;
#define MAXN 100005
#define MAXM 200005
#define pb push_back
#define mkpr make_pair
#define fir first
#define sec second
#define lowbit(x) (x&-x)
const int mo=998244353;
const int inv2=5e8+4;
const int jzm=2333;
const int zero=15;
const LL INF=0x3f3f3f3f3f3f3f3f;
const double Pi=acos(-1.0);
const double eps=1e-9;
const int lim=1000000;
const int orG=3,ivG=332748118;
const int n1=500;
const int M=MAXN/n1+5,N=n1+5;
template<typename _T>
_T Fabs(_T x){return x<0?-x:x;}
template<typename _T>
void read(_T &x){
    _T f=1;x=0;char s=getchar();
    while(s<'0'||s>'9'){if(s=='-')f=-1;s=getchar();}
    while('0'<=s&&s<='9'){x=(x<<3)+(x<<1)+(s^48);s=getchar();}
    x*=f;
}
int add(int x,int y,int p){return x+y<p?x+y:x+y-p;}
void Add(int &x,int y,int p){x=add(x,y,p);}
int qkpow(int a,int s,int p){int t=1;while(s){if(s&1)t=1ll*a*t%p;a=1ll*a*a%p;s>>=1;}return t;}
int n,m,a[MAXN],block[MAXN],L[M],R[M],b[M][N],len[M];
int val[N][M],sum[N][M],pid[MAXN],ta[N],tb[N],lena,lenb;
int rg[N][N],psum[M][MAXN],pd[N][N],d[N];
LL ans[MAXM],pre[N][M];
struct ming{int l,r,x,y;}s[MAXM];
bool cmp(int x,int y){return a[x]<a[y];}
int main(){
    read(n);read(m);
    for(int i=1;i<=n;i++)read(a[i]);
    for(int i=1;i<=n;i++)block[i]=(i+n1-1)/n1;
    for(int i=1;i<=n;i++){if(!L[block[i]])L[block[i]]=i;R[block[i]]=i;}
    for(int i=1;i<=block[n];i++){
        len[i]=R[i]-L[i]+1;
        for(int j=1;j<=len[i];j++)
            b[i][j]=j+L[i]-1;
        sort(b[i]+1,b[i]+len[i]+1,cmp);
    }
    for(int i=1;i<=m;i++)read(s[i].l),read(s[i].r),read(s[i].x),read(s[i].y);
    for(int i=1;i<=block[n];i++)
        for(int j=1,k=1;j<=n;j++){
            if(a[b[i][k]]==j)k++;
            psum[i][j]=psum[i-1][j]+k-1;
        }
    for(int i=1;i<=block[n];i++){
        for(int j=L[i];j<=R[i];j++)d[j-L[i]+1]=a[j];
        for(int j=1;j<i;j++)
            for(int ki=1,kj=1;ki<=len[i]||kj<=len[j];)
                if(ki>len[i]||(kj<=len[j]&&a[b[j][kj]]<a[b[i][ki]]))kj++;
                else val[ki][j]=kj-1,ki++;
        for(int j=1;j<=len[i];j++)
            for(int k=1;k<i;k++)
                sum[j][k]=sum[j][k-1]+val[j][k];
        for(int j=1;j<=len[i];j++)for(int k=1;k<i;k++)
            pre[j][k]=pre[j-1][k]+sum[j][k];
        for(int j=n,k=len[i];j>0;j--){if(j<a[b[i][k]]&&k)k--;pid[j]=k;}
        for(int ltn=2;ltn<=len[i];ltn++)
            for(int l=1,r=ltn;r<=len[i];l++,r++)
                rg[l][r]=rg[l+1][r]+rg[l][r-1]-rg[l+1][r-1]+(b[i][l]<b[i][r]);
        for(int j=1;j<=len[i];j++)
            for(int k=1;k<j;k++)
                pd[j][k]=pd[j][k-1]+(d[j]>d[k]);
        for(int j=1;j<=m;j++){
            if(block[s[j].l]<i&&i<block[s[j].r]){
                int al=block[s[j].l],ar=i-1;
                ans[j]+=pre[pid[s[j].y]][ar]-pre[pid[s[j].y]][al];
                ans[j]-=pre[pid[s[j].x-1]][ar]-pre[pid[s[j].x-1]][al];
                ans[j]-=1ll*(pid[s[j].y]-pid[s[j].x-1])*(psum[ar][s[j].x-1]-psum[al][s[j].x-1]);
                ans[j]+=rg[pid[s[j].x-1]+1][pid[s[j].y]];
                continue;
            }
            if(block[s[j].l]==i&&block[s[j].r]==i){
                int l=s[j].l-L[i]+1,r=s[j].r-L[i]+1,now=0;
                for(int k=l;k<=r;k++){
                    if(s[j].x<=d[k]&&d[k]<=s[j].y)
                        ans[j]+=pd[k][k-1]-pd[k][l-1]-now;
                    if(d[k]<s[j].x)now++;
                }
                continue;
            }
            if(block[s[j].l]==i){
                int l=s[j].l-L[i]+1,r=len[i],p=block[s[j].r]-1,now=0;
                for(int k=l;k<=r;k++){
                    if(s[j].x<=d[k]&&d[k]<=s[j].y){
                        ans[j]+=pd[k][k-1]-pd[k][l-1]-now;
                        ans[j]+=psum[p][s[j].y]-psum[p][d[k]]-psum[i][s[j].y]+psum[i][d[k]];
                    }
                    if(d[k]<s[j].x)now++;
                }
                continue;
            }
            if(block[s[j].r]==i){
                int l=1,r=s[j].r-L[i]+1,p=block[s[j].l],now=0;
                for(int k=l;k<=r;k++){
                    if(s[j].x<=d[k]&&d[k]<=s[j].y){
                        ans[j]+=pd[k][k-1]-pd[k][l-1]-now;
                        ans[j]+=psum[i-1][d[k]]-psum[i-1][s[j].x-1]-psum[p][d[k]]+psum[p][s[j].x-1];
                    }
                    if(d[k]<s[j].x)now++;
                }
                continue;
            }
        }
    }
    for(int i=1;i<=m;i++){
        if(block[s[i].l]==block[s[i].r])continue;lena=lenb=0;
        int bl=block[s[i].l],br=block[s[i].r];
        for(int j=1;j<=len[bl];j++)if(b[bl][j]>=s[i].l)ta[++lena]=a[b[bl][j]];
        for(int j=1;j<=len[br];j++)if(b[br][j]<=s[i].r)tb[++lenb]=a[b[br][j]];
        for(int j=1,k=1,num=0;j<=lena||k<=lenb;)
            if(j>lena||(k<=lenb&&tb[k]<ta[j])){
                if(s[i].x<=tb[k]&&tb[k]<=s[i].y)ans[i]+=num;k++;
            }
            else num+=(s[i].x<=ta[j]&&ta[j]<=s[i].y),j++;
    }
    for(int i=1;i<=m;i++)printf("%lld\n",ans[i]);
    return 0;
}

谢谢!!!

  • 4
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值