CF1637H
分析贡献,子序列内部贡献和对外面的贡献分开算。
取下标为 \(i_1 , i_2 , \cdots , i_k\) 的子序列,设 \(d_i = \sum_{j=1}^{i-1} [p_j>p_i] - [p_j<p_i>]\) ,
则外部贡献为 \(\sum d_{i_1}\) 。
内部贡献为 \({k \choose 2} - 2 \operatorname{inv}(\{p_{i_j}\})\) 。
上面都是我能做的
然后出现了性质:对于原序列中的逆序对 \((i,j)\) ,只选 \(j\) 是比只选 \(i\) 不劣的。
证明见:https://www.cnblogs.com/PinkRabbit/p/CF1637.html
大概是反正,取距离最近一组不合法数对,分析贡献。
那么最终选得的每一个数,其后的所有逆序对都必选。
因此 \(\operatorname{inv}(\{p_{i_j}\}) = \sum_{j=1}^k \sum_{k=i_j+1}^n [p_k < p_{i_j}]\) 。
因此一个数的贡献就是 \(c_i = d_i - \sum_{k=i+1}^n [p_k < p_i]\) 。
排序后贪心取前 \(k\) 个。
结论的作用应该是把贡献具体到每个数上了。
#include<bits/stdc++.h>
#define fo(i,a,b) for(int i=a;i<=b;++i)
#define fd(i,a,b) for(int i=a;i>=b;--i)
using namespace std;
typedef long long ll;
const int N=5e5+10;
int T,n,a[N],c[N],tr[N];
inline int query(int x){int ret=0; for(;x;x&=(x-1))ret+=tr[x]; return ret;}
inline void add(int x,int v){for(;x<=n;x+=(x&(-x)))tr[x]+=v;}
int main(){
freopen("data.in","r",stdin);
freopen("data.out","w",stdout);
scanf("%d",&T);
while(T--){
scanf("%d",&n);
fo(i,1,n)scanf("%d",&a[i]),tr[i]=c[i]=0;
ll sum=0;
fd(i,n,1){
int v=query(a[i]);
sum+=v;
c[i]=(n-a[i])-(n-i-v);
c[i]-=2*v+(a[i]-1-v);
add(a[i],1);
}
sort(c+1,c+n+1,greater<int>());
ll s=0;
printf("%lld ",sum);
fo(i,1,n){
s+=c[i];
printf("%lld ",sum-s-(ll)i*(i-1)/2);
}
printf("\n");
}
return 0;
}