洛谷P7910插入排序

好久没写题解了,今天肝一篇。

(下午的时候脑袋昏昏沉沉,写题目没有想清楚,竟然被虐了2个小时,心态炸了)

1.审题。

题目很大,你忍一下。

简单地翻译一下题目 。维护一个数组a,a中的所有元素均为非负整数。有两种操作,操作1给出两个数字x,v,意为将a_{x}修改为v,修改会被保存;操作2给出一个数字,要求输出a_{x}是a中的第几小,修改不被保存其中操作1的次数不超过5000次。

此处留下5分钟,诸君先独立思考再看题解——

2.思考算法。

注意红字,操作1的次数不超过5000次。结合n的大小8000,这说明什么?操作1可以使用O(n)的时间复杂度(5e3*8e3=4e7<1e8)!但是,代价为操作2的时间复杂度必须为O(1)或O(logn)。

 先思考操作1的O(n)算法。结合题目的背景——插入排序。于是我们可以维护一个结构体b,包含cnt与num两个int类型的数据。其中cnt表示数字大小,num表示这在原来是第几个元素。每次更改数字大小,只要找到满足num==x的b,再进行插入排序,就完美解决了操作1。

不可能,绝对不可能!

的确,我们还没有解决一个问题——遇到两个大小相同的数字怎么办?那么,回想一下,插入排序是怎么排序的——每次处理两个相邻的数字,若与排序顺序相反,则交换。因为插入排序每次只会比较两个相邻的数字,所以不存在两个大小相同的数字,在排序之前与排序之后的相对位置不同的情况,即该算法具有稳定性

现在,你一定会    茅厕顿开    茅塞顿开。所以num的存储在这里还有一番大用处!对于两个cnt相同的数字,num小的排在前面。即cnt为第一关键字,num为第二关键字。所以,我们的一部分代码就此成型——

​
……

struct node{
	int cnt,num;//已经解释过了
}b[8080];
bool f(node x,node y){//f函数相当于重载了<
	return x.cnt==y.cnt ? x.num<y.num : x.cnt<y.cnt;
}

……

for(int i=1;i<=n;i++){
	scanf("%d",&b[i].cnt);
	b[i].num=i;//初始化成i
}

int op,x,v;
scanf("%d",&op);
if(op==1){//这段代码想必都能看懂
	scanf("%d%d",&x,&v);
	int i=1;
	for(;i<=n;i++) if(b[i].num==x) break;
	b[i].cnt=v;
	while(i>1){//往前插入
		if(f(b[i],b[i-1])){
			swap(b[i],b[i-1]);
			i--;
		}else{
			break;
		}
	}
	while(i<n){//往后插入
		if(f(b[i+1],b[i])){
			swap(b[i],b[i+1]);
			i++;
		}else{
			break;
		}
	}
}

​

于是,剩下的一部分也是轻松拿捏——这怎么写O(1)?(被动语态石锤)

显而易见,如果每次输出都要重新查询这个数组,那么整体的时间复杂度就是O(nq),2e5*8e3=1.6e9,超时到了姥姥家。于是,思考一下怎么加快速度。

以寻常的思路,空间换时间——开一个c数组,其中c_{i}表示第i个数字在排序后的第几个位置(即答案)。那么,在处理b的时候,只需要顺便给c修改一下就可以O(1)回答了。代码(别无脑抄)——

struct ele {
	int cnt;
	int num;
} b[8080];
int c[8080];

bool f(ele x, ele y) {
	return x.cnt == y.cnt ? x.num < y.num : x.cnt < y.cnt;
}

signed main() {
	int n, q;
	scanf("%d%d", &n, &q);
	for (int i = 1; i <= n; i++) {
		scanf("%d", &b[i].cnt);
		b[i].num = i;
		c[i] = i;
	}
    1.__________;//程序填空1(50分)
	for (int i = 1; i <= n; i++) {
		c[b[i].num] = i;
	}
	while (q--) {
		int op, x, v;
		scanf("%d%d", &op, &x);
		if (op == 1) {
			scanf("%d", &v);
			int t=c[x];//t就是原始位置
            2.________;//程序填空2(50分)
			while(t>1){
				if(f(b[t],b[t-1])){
					c[b[t].num]--;
					c[b[t-1].num]++;
					swap(b[t],b[t-1]);
					t--;
				}else{
					break;
				}
			}
			while(t<n){
				if(f(b[t+1],b[t])){
					c[b[t].num]++;
					c[b[t+1].num]--;
					swap(b[t],b[t+1]);
					t++;
				}else{
					break;
				}
			}//朴实无华
		} else {
			printf("%d\n", c[x]);//O(1)输出
		}
	}
}

​

同时祝愿诸君国庆快乐!

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值