nowcoder 2019noip暑假7天营Day3 T2点与面
T2 点与面
我以为牛客网上的题解讲的还不错,便在其基础上补一些细节。
作者:Asuka_Minato
链接:https://ac.nowcoder.com/discuss/229199?type=101&order=0&pos=1&page=1
来源:牛客网
我们考虑枚举W型最中间的点,既\(p_3\),我们考虑\(p_3\)的左边,要找一个比\(p_3>p_2\),还要找一个\(p_1>p_2\),那么实际上,对于\(p_3\)左侧,低于\(p_3\)的点\(p_2\),对答案的贡献为\(p_2\)左侧比\(p_2\)大的点,我们这一步可以用BIT维护,得到左右两边的贡献之后,使用乘法原理将他们组合,就能计算出答案。
统计\(p_2\)时,直接枚举是单次\(O(nlogn)\)的,总复杂度\(O(n2logn)\)可以得到40%的分数,考虑我们刚才维护的信息,和这个类似,我们也可以用类似的方式,再维护一颗BIT来完成统计。 得到左右两边的贡献之后,使用乘法原理将他们组合,就能计算出答案。
总时间复杂度可以做到\(O(nlogn)\),拿到全部分数.
此处的类似方式,考虑左边的情况,即用一棵BIT,维护每一个值的逆序对个数,因为过程为边查询边插入,所以每一次查询实际上查询的是当前下标情况下对所有小于当前值的逆序对总和,顺序对亦然。
代码如下:
#include<bits/stdc++.h>
using namespace std;
#define int long long
int const MAXN=1e5+10,MAXM=1e6,mod=998244353;
int n,maxn,ans;
int Y[MAXN];
int a[MAXM],b[MAXM];
int ni[MAXN],shun[MAXN];
int lowbit(int x){return x & (-x);}
void inserta(int x,int y){
for(int i=x;i<=maxn;i+=lowbit(i))a[i]+=y;
}
void insertb(int x,int y){
for(int i=x;i<=maxn;i+=lowbit(i))b[i]+=y;
}
int aska(int x){
int sum=0;
for(;x;x-=lowbit(x))sum+=a[x];
return sum;
}
int askb(int x){
int sum=0;
for(;x;x-=lowbit(x))sum+=b[x];
return sum;
}
signed main(){
scanf("%lld",&n);
for(int i=1;i<=n;i++){
scanf("%lld",&Y[i]);maxn=max(maxn,Y[i]);
}
for(int i=1;i<=n;i++){
inserta(Y[i],1);
insertb(Y[i],aska(maxn)-aska(Y[i]));//插入i的逆序对个数
ni[i]=askb(Y[i]-1)%mod;//查询满足比Y[i]小且在左边的所有点的逆序对总和
}
memset(a,0,sizeof(a));memset(b,0,sizeof(b));
for(int i=n;i>=1;i--){
inserta(Y[i],1);
insertb(Y[i],aska(maxn)-aska(Y[i]));//插入i的顺序对个数
shun[i]=askb(Y[i]-1)%mod;//查询满足比Y[i]小且在右边的所有点的顺序对总和
}
for(int i=1;i<=n;i++){
ans=(ans+(shun[i]*ni[i])%mod)%mod;
}
printf("%lld\n",ans);
return 0;
}