• 2021icpc南京 D


    题意:

    给出如下排序长度为 l e n len len的数组代码:

    for(int i=1;i<=len;i++)
        for(int j=1;j<=len;j++)
            if(a[i]<a[j]) swap(a[i],a[j]);
    
    • 1
    • 2
    • 3

    给出一个长 n n n的数组 a a a,求排序 [ 1 , k ] [1,k] [1,k]区间的交换次数, k = 1 , 2 , . . . . , i k=1,2,....,i k=1,2,....,i

    Solution:

    显然我们需要模拟一便这个排序,不妨直接写一个这个程序,输出。

    发现排序长度为 l e n len len时,有如下性质,第 i i i次会把最大值交换到 i i i处,且在第一次之后,由于从从前往后交换,最大值目前位于 i − 1 i-1 i1处,于是这一次发生的交换次数为 [ 1 , i − 1 ] [1,i-1] [1,i1]大于 a [ i ] a[i] a[i]的数的种数,因为相等的元素不交换。这个是可以用树状数组维护的,但这样的时间复杂度是 O ( n 2 ) O(n^2) O(n2)的,不妨考虑一下相邻两个前缀有什么关系,下面考虑两个前缀 [ 1 , i − 1 ] [1,i-1] [1,i1] [ 1 , i ] [1,i] [1,i]的关系,并且叫第一次暴力处理时发生交换的位置为 [ p 1 , p 2 , . . . , p m ] [p_{1},p_{2},...,p_{m}] [p1,p2,...,pm],最后的结果的 p i p_{i} pi的会到 p i + 1 p_{i+1} pi+1去, p m p_{m} pm会到 p 1 p_{1} p1去,暴力处理之后的每一次都是把最大值交换到 i i i就不发生交换了,不难发现 [ p 1 , p 2 , . . . , p m ] [p_{1},p_{2},...,p_{m}] [p1,p2,...,pm]这些位置就是前缀最大值的变化点,于是发生变化一定在此处,即 a [ i ] > m a x ( a [ 1 ] , a [ 2 ] , . . . , a [ i − 1 ] ) a[i]>max(a[1],a[2],...,a[i-1]) a[i]>max(a[1],a[2],...,a[i1])时,这样比较复杂,不妨按照这样的思路,先考虑:

    a [ i ] < m a x ( a [ 1 ] , a [ 2 ] , . . . , a [ i − 1 ] ) a[i]a[i]<max(a[1],a[2],...,a[i1])时:

    此时最大值已经在 [ 1 , i − 1 ] [1,i-1] [1,i1]被找出来了,所以我们只需要在最大值在 i − 1 i-1 i1时把最大值移到 i i i即可,即

    a n s [ i ] = a n s [ i − 1 ] + c o u n t ( a [ i ] + 1 , n ) ans[i]=ans[i-1]+count(a[i]+1,n) ans[i]=ans[i1]+count(a[i]+1,n)

    其中 c o u n t ( x , y ) count(x,y) count(x,y) [ 1 , i − 1 ] [1,i-1] [1,i1]中有多少数是 ∈ [ x , y ] \in[x,y] [x,y]的,这个可以用树状数组同时维护

    a [ i ] = m a x ( a [ 1 ] , a [ 2 ] , . . . , a [ i − 1 ] ) a[i]=max(a[1],a[2],...,a[i-1]) a[i]=max(a[1],a[2],...,a[i1])时,最大值都不需要移到 i i i,于是:

    a n s [ i ] = a n s [ i − 1 ] ans[i]=ans[i-1] ans[i]=ans[i1]

    最后再来考虑 a [ i ] > m a x ( a [ 1 ] , a [ 2 ] , . . . , a [ i − 1 ] ) a[i]>max(a[1],a[2],...,a[i-1]) a[i]>max(a[1],a[2],...,a[i1])

    由于 [ 1 , i − 1 ] [1,i-1] [1,i1]的最大值不为 a [ i ] a[i] a[i],但是还是存在一个最大值,替 a [ i ] a[i] a[i]完成了交换工作,只需要在第一次暴力处理的时候把 a [ i ] a[i] a[i]交换到1,就完成了把最大值更新为 a [ i ] a[i] a[i]的工作,又最大值此时要从 i − 1 i-1 i1被交换到 i i i处,这两个工作需要2的交换次数。并且由于修改最大值,之前的最大值有多个的情况下互相不会计数,而现在他们都需要和现在的最大值计数了,并且最大值第二次出现的位置pos之后的区间 [ p o s , i − 1 ] [pos,i-1] [pos,i1]每个数都要与新最大值交换,于是此时有

    $$
    ans[i]=ans[i-1]+f[max(1,i-1)]*(i-1-second[max(1,i-1)]+1)+2

    $$

    这个思维难度有点大。。

    // #include
    #include
    #include
    #include
    #include
    #include
    #include
    #include
    #include
    #define endl '\n'
    using namespace std;
    
    using ll=long long;
    const int N=100005,inf=0x3fffffff;
    const long long INF=0x3f3f3f3f3f3f,mod=998244353;
    
    int a[N],tot[N];
    ll n,ans[N],tree[N];
    bool vis[N];
    
    inline int lowbit(int x){return -x&x;}
    
    void add(int x,int val)
    {
    	if(vis[x]) return;
    	vis[x]=true;
    	while(x<=n)
    	{
    		tree[x]+=val;
    		x+=lowbit(x);
    	}
    }
    
    ll getsum(int x)
    {
    	ll ret=0;
    	while(x)
    	{
    		ret+=tree[x];
    		x-=lowbit(x);
    	}
    	return ret;
    }
    
    int first[N],second[N];
    ll query(int l,int r){return getsum(r)-getsum(l-1);}
    
    void work()
    {
    	cin>>n;
    	for(int i=1;i<=n;i++)
    	{
    		cin>>a[i];
    		tree[i]=vis[i]=tot[i]=first[i]=second[i]=0;
    	}
    	int max1=a[1];
    	add(a[1],1); first[a[1]]=1;
    	for(int i=2;i<=n;i++)
    	{
    		if(a[i]==max1) ans[i]=ans[i-1];
    		else if(a[i]<max1) ans[i]=ans[i-1]+query(a[i]+1,n);
    		else ans[i]=ans[i-1]+(second[max1]!=0)*(i-second[max1])+2;
    		add(a[i],1); max1=max(max1,a[i]);
    		if(!first[a[i]]) first[a[i]]=i;
    		else if(!second[a[i]]) second[a[i]]=i;
    	}
    	for(int i=1;i<=n;i++)
    	{
    		cout<<ans[i];
    		if(i<n) cout<<" ";
    		add(a[i],-1); 
    	}
    	cout<<endl;
    }	
    
    int main()
    {
    	ios::sync_with_stdio(false);
    	int t; cin>>t;
    	while(t--) work(); 
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
  • 相关阅读:
    AVL树性质和实现
    CDH 03MySQL5.7安装配置(markdown新版)
    爱上开源之boot4go-dockerui-剧透篇
    ESP32网络开发实例-TCP服务器数据传输
    软考正高级职称评审条件
    STL常用容器——deque容器的使用
    JUC P3 共享模型之无锁同步,CAS,原子类,Unsafe类 基础+代码
    算法打卡day31|贪心算法篇05|Leetcode 435. 无重叠区间、763.划分字母区间、56. 合并区间
    开发中遇到的一个bug
    微软远程桌面服务远程代码执行漏洞
  • 原文地址:https://blog.csdn.net/stdforces/article/details/127455162