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;
}
posted @ 2022-11-23 12:13  Kelvin2005  阅读(29)  评论(0编辑  收藏  举报