• LG6168 [NOI Online #1 提高组] 冒泡排序(线段树/树状数组)


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

    这个日好像不难,首先我们可以手算几组冒泡,我们可以发现一个特别的性质。

    我们定义 \(c_i\),满足:

    \[c_i=\sum\limits_{j=1}^{i-1} [a_j>a_i] \]

    我们会发现,每进行一次冒泡,每个 \(c_i\) 都会减一,除非 \(c_i\) 已经等于 \(0\) 了。为什么呢?我们从冒泡排序的本质出发,每一轮对于 \([1,r]\) 这一个区间,是把这个区间内最大的数移到了最后一个位置。假设这个最大的数位置为 \(x\)。那么 \([x,r]\) 区间内每个 \(c_i\) 减小 \(1\) 很好理解。那么对于 \([1,x]\) 呢?那不是和 \([1,r]\) 一样的吗?所以递归证明即可。

    有了这个性质,我们有:

    \[ans=\sum\limits_{i=1}^n \max(0,c_i-k) \]

    我们用权值线段树可以求出 \(c_i\),清空后,还可以再次也可以再次用于快速求出 \(ans\)就不想用树状数组哼!

    时间复杂度 \(O(n\log n+m\log n)\)

    //Don't act like a loser.
    //This code is written by huayucaiji
    //You can only use the code for studying or finding mistakes
    //Or,you'll be punished by Sakyamuni!!!
    //#pragma GCC optimize("Ofast","-funroll-loops","-fdelete-null-pointer-checks")
    //#pragma GCC target("ssse3","sse3","sse2","sse","avx2","avx")
    #include<bits/stdc++.h>
    #define int long long
    using namespace std;
    
    int read() {
    	char ch=getchar();
    	int f=1,x=0;
    	while(ch<'0'||ch>'9') {
    		if(ch=='-')
    			f=-1;
    		ch=getchar();
    	}
    	while(ch>='0'&&ch<='9') {
    		x=x*10+ch-'0';
    		ch=getchar();
    	}
    	return f*x;
    }
    
    const int MAXN=2e5+10; 
    
    int n,m;
    int a[MAXN],c[MAXN];
    struct seg_tree {
    	int sum,num;
    }s[MAXN<<2];
    
    void modify(int l,int r,int p,int x,int val) {
    	if(r<x||x<l) {
    		return ;
    	}
    	if(l==r) {
    		s[p].num+=val;
    		s[p].sum+=val*l;
    		return ;
    	}
    	int mid=(l+r)>>1;
    	modify(l,mid,p<<1,x,val);
    	modify(mid+1,r,p<<1|1,x,val);
    	s[p].sum=s[p<<1].sum+s[p<<1|1].sum;
    	s[p].num=s[p<<1].num+s[p<<1|1].num;
    }
    int query_num(int l,int r,int p,int x,int y) {
    	if(r<x||y<l) {
    		return 0;
    	}
    	if(x<=l&&r<=y) {
    		return s[p].num;
    	}
    	int mid=(l+r)>>1;
    	return query_num(l,mid,p<<1,x,y)+query_num(mid+1,r,p<<1|1,x,y);
    }
    int query_sum(int l,int r,int p,int x,int y) {
    	if(r<x||y<l) {
    		return 0;
    	}
    	if(x<=l&&r<=y) {
    		return s[p].sum;
    	}
    	int mid=(l+r)>>1;
    	return query_sum(l,mid,p<<1,x,y)+query_sum(mid+1,r,p<<1|1,x,y);
    }
    
    signed main() {
    	cin>>n>>m;
    	for(int i=1;i<=n;i++) {
    		cin>>a[i];
    		c[i]=query_num(1,n,1,a[i]+1,n);
    		modify(1,n,1,a[i],1);
    	}
    	memset(s,0,sizeof s);
    	
    	for(int i=1;i<=n;i++) {
    		modify(1,n,1,c[i],1);
    	}
    	
    	while(m--) {
    		int opt,x;
    		opt=read(),x=read();
    		if(opt==1) {
    			modify(1,n,1,c[x],-1);
    			modify(1,n,1,c[x+1],-1);
    			if(a[x]>a[x+1]) {
    				swap(c[x],c[x+1]);
    				swap(a[x],a[x+1]);
    				c[x]--;
    			}
    			else {
    				swap(c[x],c[x+1]);
    				swap(a[x],a[x+1]);
    				c[x+1]++;
    			}
    			modify(1,n,1,c[x],1);
    			modify(1,n,1,c[x+1],1);
    		}
    		else {
    			printf("%lld\n",query_sum(1,n,1,x+1,n)-x*query_num(1,n,1,x+1,n));
    		}
    	}
    	return 0;
    }
    
    
  • 相关阅读:
    ELF文件格式解析
    了解ELF文件类型
    汇编程序-简介
    汇编指令入门级整理
    双系统: Windows10时间快和Ubuntu时间差问题
    iDRAC on DELL Server
    ubuntu WIFI 连接不稳定(wifi无法连接问题)
    删除字符串中的空格(空字符)
    VBS 基础知识
    VBS 操作Excel的常用方法
  • 原文地址:https://www.cnblogs.com/huayucaiji/p/LG6168.html
Copyright © 2020-2023  润新知