• 【洛谷P6186】[NOI Online 提高组] 冒泡排序


    题目

    题目链接:https://www.luogu.com.cn/problem/P6186
    给定一个 \(1 ∼ n\) 的排列 \(p_i\),接下来有 \(m\) 次操作,操作共两种:

    1. 交换操作:给定 \(x\),将当前排列中的第 \(x\) 个数与第 \(x+1\) 个数交换位置。
    2. 询问操作:给定 \(k\),请你求出当前排列经过 \(k\) 轮冒泡排序后的逆序对个数。
      对一个长度为 \(n\) 的排列 \(p_i\) 进行一轮冒泡排序的伪代码如下:
    for i = 1 to n-1:
      if p[i] > p[i + 1]:
        swap(p[i], p[i + 1])
    

    思路

    观察冒泡排序的伪代码,容易发现每一轮冒泡排序,会把最大的数移动到最后面,设 \(cnt[i]\) 表示第 \(i\) 个数前严格大于他的数字个数,手画一下可以发现,一轮冒泡排序会把每一个 \(cnt[i]>1\)\(cnt[i]-1\),对于 \(cnt[i]=0\) 的不变。
    那么对于一个查询 \(k\) 轮冒泡排序之后的逆序对个数的操作,设冒泡前的逆序对数量为 \(sum\),那么 \(k\) 轮排序后:

    1. \(cnt[i]\leq k\),那么 \(cnt[i]\) 会清零,这一部分减少了 \(\sum_{cnt[i]\leq k} cnt[i]\) 个逆序对。
    2. \(cnt[i]>k\),那么 \(cnt[i]\) 会减少 \(k\),这一部分减少了 \(k\times \sum^{n}_{i=1}[cnt[i]>k]\) 个逆序对。

    树状数组分别维护两者的前缀和即可。
    对于修改操作,设修改位置 \(i\),显然受影响的就只有 \(cnt[i]\)\(cnt[i+1]\)。分类讨论 \(a[i]\)\(a[i+1]\) 的大小即可。
    时间复杂度 \(O(n\log n)\)

    代码

    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    using namespace std;
    typedef long long ll;
    
    const int N=200010;
    int n,m,opt,k,cnt0,a[N],cnt[N];
    ll sum;
    
    struct Bit
    {
    	ll c[N];
    	Bit() { memset(c,0,sizeof(c)); }
    	
    	void add(int x,ll k)
    	{
    		if (!x) return;
    		for (int i=x;i<=n;i+=i&-i)
    			c[i]+=k;
    	}
    	
    	ll ask(int x)
    	{
    		ll ans=0;
    		for (int i=x;i;i-=i&-i)
    			ans+=c[i];
    		return ans;
    	}
    }bitc,bits,bit;
    
    int main()
    {
    	freopen("data.txt","r",stdin);
    	scanf("%d%d",&n,&m);
    	for (int i=1;i<=n;i++)
    	{
    		scanf("%d",&a[i]);
    		cnt[i]=bit.ask(n)-bit.ask(a[i]);
    		bit.add(a[i],1);
    		bitc.add(cnt[i],1); bits.add(cnt[i],cnt[i]);
    		sum+=cnt[i];
    		if (!cnt[i]) cnt0++;
    	}
    	while (m--)
    	{
    		scanf("%d%d",&opt,&k);
    		if (opt==1)
    		{
    			bitc.add(cnt[k],-1); bits.add(cnt[k],-cnt[k]);
    			bitc.add(cnt[k+1],-1); bits.add(cnt[k+1],-cnt[k+1]);
    			if (a[k]>a[k+1])
    			{
    				cnt[k+1]--; sum--;
    				if (!cnt[k+1]) cnt0++;
    			}
    			if (a[k]<a[k+1])
    			{
    				if (!cnt[k]) cnt0--;
    				cnt[k]++; sum++;
    			}
    			swap(cnt[k],cnt[k+1]); swap(a[k],a[k+1]);
    			bitc.add(cnt[k],1); bits.add(cnt[k],cnt[k]);
    			bitc.add(cnt[k+1],1); bits.add(cnt[k+1],cnt[k+1]);
    		}
    		else
    		{
    			if (k>n) k=n;
    			ll ans1=bits.ask(k);
    			ll ans2=1LL*(n-bitc.ask(k)-cnt0)*k;
    			printf("%lld\n",sum-ans1-ans2);
    		}
    	}
    	return 0;
    }
    
  • 相关阅读:
    如何很好的使用Linq的Distinct方法
    根据字符串获取对应类型(Type) 转
    .Net 读取xml
    认识ASP.NET MVC的5种AuthorizationFilter
    使用admin插入数据失败
    乱序批量精确修改文件名
    多进程+协程方案处理高IO密集,提升爬取效率
    Linux 安装 CMake
    Ubuntu 截图工具deepin-screenshot添加使用
    Linux virtualenv .bashrc配置文件
  • 原文地址:https://www.cnblogs.com/stoorz/p/12449953.html
Copyright © 2020-2023  润新知