ABC 306 F 题解

原题传送门#


题意:给定n个序列,每个序列有m个数。且这n * m个数互不相同。

定义f(A,B):将A、B两个数组合在一起升序排列后,记为数组C。
则f(A,B)为A数组中所有数在C数组中的下标之和。
求对于所有1<=i<j<=n,f(i,j)的和。


性质:可以大胆地排序,因为它只关心相对位置

硬算显然T飞,考虑批量计算贡献。

再看i和j的限制,十分灵性啊,一眼:倒着搞,就可以对于每一个i,把大于它的j都算进去,且只需要搞一遍。

每次只不过是查询一下排名罢了,随便选个ds就行了,比如优美的树状数组。当然我懒,直接复制了自己以前写的Treap板子(别学我

但是要注意,对于每个数在计算贡献的时候还要加上它之后(也就是此时ds里面已经维护了的(不包含当前数组))数组的数量,这个简单地打个草稿就能理解。

然后就结束了(你看这场的F是不是很水

还是贴个码,

#include<bits/stdc++.h>
using namespace std;
#define re  
#define int long long
const int N=10010,M=110;
const int INF=1e10;
int idx,root;
int n,m,a[N][M],ans;
struct node{
	int l,r;
	int key,val;
	int cnt,size;
}tr[N*M];
int get_node(int key){
	tr[++idx].key=key;
	tr[idx].val=rand();
	tr[idx].cnt=1;
	tr[idx].size=1;
	return idx;
}
void push_up(int id){
	tr[id].size=tr[tr[id].l].size+tr[tr[id].r].size+tr[id].cnt;
}
void build(){
	root=get_node(-INF),tr[1].r=get_node(INF);
	push_up(root);
}
void zig(int &p){
	int q=tr[p].r;
	tr[p].r=tr[q].l;
	tr[q].l=p;
	p=q;
	push_up(tr[p].l);
	push_up(p);
}
void zag(int &p){
	int q=tr[p].l;
	tr[p].l=tr[q].r;
	tr[q].r=p;
	p=q;
	push_up(tr[p].r);
	push_up(p);
}
void insert(int x,int &p){
	if(!p){
		p=get_node(x);
		return;
	}
	if(tr[p].key==x)tr[p].cnt++;
	else{
		if(x<tr[p].key){
			insert(x,tr[p].l);
			if(tr[tr[p].l].val>tr[p].val)zag(p);
		}
		else{
			insert(x,tr[p].r);
			if(tr[tr[p].r].val>tr[p].val)zig(p);
		}
	}
	push_up(p);
}
int get_rank(int x,int p){
	if(!p)return 0;
	if(tr[p].key==x)return tr[tr[p].l].size+1;
	if(x<tr[p].key)return get_rank(x,tr[p].l);
	return tr[tr[p].l].size+tr[p].cnt+get_rank(x,tr[p].r);
}
inline int read(){
    re int x=0,f=1;char c=getchar();
    while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
    while(c>='0'&&c<='9')x=(x<<3)+(x<<1)+(c^48),c=getchar();
    return x*f;
}
signed main(){
	build();
	n=read(),m=read();
	for(re int i=1;i<=n;i++)for(re int j=1;j<=m;j++)a[i][j]=read();
	for(re int i=1;i<=m;i++)insert(a[n][i],root);
	for(re int i=n-1;i;i--){
		sort(a[i]+1,a[i]+1+m);
		for(re int j=1;j<=m;j++){
			int g=get_rank(a[i][j],root)-1;
			ans+=g+(n-i)*j;
		}
		for(re int j=1;j<=m;j++)insert(a[i][j],root);
	}
	printf("%lld",ans);
	return 0;
}
posted @   MrcFrst  阅读(23)  评论(0编辑  收藏  举报
编辑推荐:
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
阅读排行:
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· 写一个简单的SQL生成工具
· AI 智能体引爆开源社区「GitHub 热点速览」
· C#/.NET/.NET Core技术前沿周刊 | 第 29 期(2025年3.1-3.9)
点击右上角即可分享
微信分享提示
主题色彩