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;
    }
    
  • 相关阅读:
    Linux集群之间配置NTP时间同步ntp
    CentOS7安装配置MariaDB(mysql)数据主从同步
    常用邮件SMTP POP3服务器地址大全
    为应用创建多个独立python运行环境
    Linux中安装配置KVM虚拟化
    Linux系统管理和调优(内存、CPU、磁盘IO、网络)
    CentOS8Linux中配置网易云网络yum源安装软件
    CentOS7linux通过http配置共享自动创建yum源的shell脚本
    BigDecimal类型比较数字大小
    Double值保留两位小数的四种方法
  • 原文地址:https://www.cnblogs.com/zmyzmy/p/13766942.html
Copyright © 2011-2022 走看看