[NOI2020]时代的眼泪
Posted StaroForgin
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[NOI2020]时代的眼泪相关的知识,希望对你有一定的参考价值。
时代的眼泪
题解
变成时代的眼泪了。
貌似是一个奇怪的区间顺序对问题。
数据范围
n
⩽
1
0
5
n\\leqslant 10^5
n⩽105,大概是树套树或者分块,感觉分块比较可行,考虑分块。
原题相当于就是求
∑
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′][y⩽pi<pj⩽y′],我们就对它们的下标分块,方便维护第一维。
然后考虑之后怎么计算答案。
首先是整块内部的贡献,由于对于一个整块,不同意义的
[
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)B⩾nn+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 处理器将成时代的眼泪!