CF1601C

· · 个人记录

吐槽:题解里为啥都是线段树,这里来提供一种其他的做法。

先做一种简单的情况,a1n 之间所有的奇数,b1n 之间所有的偶数(原题直接讲涉及到元素不存在与元素重复的元素,会了这个后直接改一改就好了):

c的逆序对数=a的逆序对数+b的逆序对数+ab合在一起的逆序对数

前两者直接树状数组算就好了,难点是后一个。

对于 b,显然升序排序一定是更优的(自己证明一下)。

一些定义: 设 x 为目前最靠右(最大)的数(根据状态动态变化)。

对于 a,我们 i1~n 考虑插入,新加入的一个,如果比 x 还要大,那么直接插在 b 中它权值的位置,不会产生贡献。接下来是比它小的情况,比较复杂,换一下行。

先将 a[i] 加入在x的右边,逆序对数贡献为 a[i] 的数值到x之间b序列中数的个数,因为现在考虑的是一个排列的情况(所有元素都出现过),所以直接是 x-a[i]

但是这样子 x 一直会变大,之后插入的数比 x 小的情况只会越来越少,咋办捏。

我们考虑移动 a[i],把 x 和它一起向左移动,一直移到 a[i] 的时候,逆序对的个数都是不变的(a[i] 贡献的 -1x 的贡献 +1)(再移 a[i] 也加,x 也加),这样子 x 就会变小,之后加进来的数比 x 大(不产生贡献)的可能就更大。

但这样有个明显的问题,在 a[i]x 之间如果有数,移动了原先放在那里的数,逆序对个数不一定相比之前还是不变(有可能增加),有了这个阻碍,移动不一定比放着不动要优。

移动or不移动?这很难决策。

接下来就到了做法的重难点,怎么维护?

我们考虑用一个堆来维护 x

先放代码再解释(注意我们说的是简单化的情况,没有重复元素,每个元素都出现过,不然放一起说比较乱):

priority_queue<int> q;
q.push(a[1]);
for(int i=2;i<=(n/2);++i){
    if(q.top()>a[i]) ans+=q.top()-a[i],q.pop(),q.push(a[i]);
    q.push(a[i]);
}

先说if里面的内容:

$q.pop(),q.push(a[i])$ 把 $x$ 移动到 $a[i]$ 的位置。 但大家应该立马发现不对了,之前不是说移动不一定优嘛? 对,你说的没错,所以这里不是真正的移动,只是我们当作了它移动。 为什么能这样呢,因为我们用的是堆,如果再次用到现在这个移动过的 $x$,只有当大于它的所有元素都没了(被假装移动了),这时我们再真正的移动它。 再解释一遍,只有当它被访问了,它才算真正的移动,也只有比它大的,也就是我们之前说的阻碍都被清空了,它才能被访问了,这样一切就变得合理起来了! 同理,$q.push(a[i])$ 是直接当作了 $a[i]$ 放到了 $c$ 中 $a[i]$ 的位置。 而且这样堆中的数字个数总会是 $i$,很合理吧。 简化版题意的代码(其实这个就是正睿的一道题目,有权限可以自己看看): [简化题意原地址](http://www.zhengruioi.com/problem/247) ```cpp int a[N],c[N]; priority_queue<int> q; int main(){ int n; scanf("%d",&n); for(int i=1;i<=n>>1;++i) scanf("%d",&a[i]),a[i]>>=1; ll ans=0; for(int i=n/2;i;--i){ for(int j=a[i]-1;j;j^=j&(-j)) ans+=c[j]; for(int j=a[i];j<=n;j+=j&(-j)) ++c[j]; } q.push(a[1]); for(int i=2;i<=n>>1;++i){ if(q.top()>a[i]) ans+=q.top()-a[i],q.pop(),q.push(a[i]); q.push(a[i]); } printf("%lld",ans); } ``` 这样这个简化的题意就做完了,考虑复杂的: 其实就是左右端点细节处理一下就好了,相信之前的内容看懂了再看下面的代码也不难,就自己理解一下吧(bushi。 ```cpp int n,m,a[N],b[N],va[N],pu,fw[N],ans; void add(int x,int d){for(;x<=n;x+=x&-x) fw[x]+=d;} int query(int x){int res=0;for(;x;x-=x&-x) res+=fw[x];return res;} void task(){ scanf("%d%d",&n,&m); for(int i=1;i<=n;i++) scanf("%d",&a[i]); for(int i=1;i<=m;i++) scanf("%d",&b[i]); ans=0; for(int i=1;i<=n;i++) va[i]=a[i]; sort(va+1,va+n+1); for(int i=1;i<=n;i++) fw[i]=0; for(int i=n;i>=1;i--){ int v=lower_bound(va+1,va+n+1,a[i])-va; ans+=query(v-1); add(v,1); } sort(b+1,b+m+1); priority_queue<int> q; for(int i=1;i<=n;i++){ int x=lower_bound(b+1,b+m+1,a[i])-b; int y=upper_bound(b+1,b+m+1,a[i])-b; if(!q.empty()&&q.top()>y) { ans+=q.top()-y; q.pop(); q.emplace(y); } q.emplace(x); } printf("%lld\n",ans); } ```