[NOI2020]时代的眼泪

Posted StaroForgin

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[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 \\textlower\\_bound lower_bound,所以需要对于每个块再预处理一下每个值会被离散化到哪个点上。
这部分是 O ( n B + n 2 B ) O\\left(nB+\\fracn^2B\\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(\\fracn^2B\\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(\\fracn^2B+(n+m)B\\geqslant n\\sqrtn+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 mingint 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]=preLinux 之父:80486 处理器将成时代的眼泪!

舍友聚会

NOI2009 植物大战僵尸

WampServer - 基本使用

Eclipse - 安装 SVN 插件

NOI2020