zoukankan      html  css  js  c++  java
  • [NOI Online #1 提高组]冒泡排序

    [NOI Online #1 提高组]冒泡排序

    description:

    题面已经说得很清楚了:
    给定一个(1 ∼ n)的排列(p_i),接下来有(m)次操作,操作共两种:
    交换操作:给定(x),将当前排列中的第(x)个数与第(x+1)个数交换位置。
    询问操作:给定(k),请你求出当前排列经过(k)轮冒泡排序后的逆序对个数。 对一个长度为(n)的排列(p_i)

    data range:

    (N<=2*10^5)

    solution:

    感觉这是一道翻译题
    我们记(c_i)表示第i个位置和前面位置形成的逆序对数
    那么显然总逆序对数为(sum{c_i})
    考虑一次冒泡排序后对(c_i)有什么影响
    举个例子:
    原数列:(4) (3) (1) (5) (2)
    (c_i)(0) (1) (2) (0) (3)
    一次冒泡后:(3) (1) (4) (2) (5)
    (c_i`):(0) (1) (0) (2) (0)
    似乎发现什么规律?
    (c_i`=max(0,c_{i+1}-1))
    为什么会这样呢?
    考虑相邻的两个数(a_i)(a_{i+1})((a_i>a_{i+1}))
    交换后会发现(c_i`=max(0,c_{i+1}-1))(因为逆序对数量减少了一个)
    那么考虑如何统计答案
    (ans=sum_{c_i>=k+1}{(c_i-k)}=sum_{c_i>=k+1}c_i-k*sum_{c_i>=k+1}1)
    于是用权值线段树或权值树状数组维护区间数的个数以及数的和就可以了
    至于交换相邻两个数的操作,直接一波分类讨论

    code:

    #include<bits/stdc++.h>
    using namespace std;
    typedef long long LL;
    const int N=2e5+5;
    int n,m,num[N],p[N];
    struct SGT
    {
    	int c[N<<2];LL s[N<<2];
    	#define lc rt<<1
    	#define rc rt<<1|1
    	inline void up(int rt){c[rt]=c[lc]+c[rc],s[rt]=s[lc]+s[rc];}
    	inline void upd(int rt,int l,int r,int pos,int v)
    	{
    		if(l==r){c[rt]+=v,s[rt]+=1ll*v*l;return;}
    		int mid=l+r>>1;
    		if(pos<=mid)upd(lc,l,mid,pos,v);
    		else upd(rc,mid+1,r,pos,v);
    		up(rt);
    	}
    	inline void query(int rt,int l,int r,int ll,int rr,int &cnt,LL &sum)
    	{
    		if(ll<=l&&r<=rr){sum+=s[rt],cnt+=c[rt];return;}
    		int mid=l+r>>1;
    		if(ll<=mid)query(lc,l,mid,ll,rr,cnt,sum);
    		if(mid<rr)query(rc,mid+1,r,ll,rr,cnt,sum);
    	}
    	#undef lc
    	#undef rc
    }A,B;
    int main()
    {
    	scanf("%d%d",&n,&m);
    	for(int i=1;i<=n;++i)
    	{
    		scanf("%d",p+i);
    		int ct=0;LL sm=0;A.query(1,0,n,p[i],n,ct,sm);
    		B.upd(1,0,n,ct,1),num[i]=ct;//这里注意num[i]可能为0,因此值域要从0开始
    		A.upd(1,0,n,p[i],1);
    	}
    	while(m--)
    	{
    		int t,c;scanf("%d%d",&t,&c);
    		if(t==2)
    		{
    			if(c>=n){puts("0");continue;}
    			int ct=0;LL sm=0;B.query(1,0,n,c+1,n,ct,sm);
    			printf("%lld
    ",sm-1ll*c*ct);
    		}
    		else
    		{
    			B.upd(1,0,n,num[c],-1),B.upd(1,0,n,num[c+1],-1);
    			p[c]>p[c+1]?--num[c+1]:++num[c];
    			swap(p[c],p[c+1]),swap(num[c],num[c+1]);
    			B.upd(1,0,n,num[c],1),B.upd(1,0,n,num[c+1],1);
    		}
    	}
    	return 0;
    }
    
  • 相关阅读:
    Spring Boot2 系列教程(二十)Spring Boot 整合JdbcTemplate 多数据源
    Spring Boot 如何给微信公众号返回消息
    Spring Boot2 系列教程(十九)Spring Boot 整合 JdbcTemplate
    Spring Boot2 系列教程(十八)Spring Boot 中自定义 SpringMVC 配置
    Spring Boot 开发微信公众号后台
    Spring Boot2 系列教程(十七)SpringBoot 整合 Swagger2
    Spring Boot2 系列教程(十六)定时任务的两种实现方式
    Spring Boot2 系列教程(十五)定义系统启动任务的两种方式
    Spring Boot2 系列教程(十四)CORS 解决跨域问题
    JavaScript二维数组
  • 原文地址:https://www.cnblogs.com/zmyzmy/p/13766942.html
Copyright © 2011-2022 走看看