zoukankan      html  css  js  c++  java
  • 「题解」Codeforces 1268C K Integers

    对于 (f(k)) 的计算,先计算把 (1sim k) 移成连续段,然后再计算逆序对数(最小交换次数使得一个排列排好序)。

    至于为什么是对的?感性理解一下,首先排好序一定要有个逆序对的代价,假如两个数之间没有相邻,则交换它们需要一步一步走过去,这样的花费就高了。

    考虑 (f(k-1) o f(k)) 新增的代价,计算逆序对数是容易的,(k) 只可能作为逆序对的第一个数,而第二个数可能的取值 ([1,k-1]) 都已经出现过,所以直接计算全局中以 (k) 为开头的逆序对数,树状数组易做到。

    计算把 (1sim k) 移成连续段的代价,有个经典结论就是移到中位数处,如果长度为偶数,移到中间两个的哪个都行。

    因为每个数不能移动到同一位置,所以代价计算会很麻烦,那就钦点让每个数可以移动到同一位置,最后再减去多余代价也就是两个等差数列即可。

    现在计算每个数移动到中位数处的距离,注意到每加入一个 (k) 之后中位数的最多会移动 (1) 个位置,且移动之后除了 (k) 以外的值对总代价的贡献不变,直接用 set 维护 (1sim k) 出现的位置然后移动就可以了。

    其实可以不用维护中位数,直接用树状数组记录哪些位置出现了数然后倍增找中位数也可以,常数更小。

    代码可能不大像说人话的样子,看看思路就好了。

    #include<iostream>
    #include<cstdio>
    #include<vector>
    #include<set>
    #define pb push_back
    #define mp std::make_pair
    #define fir first
    #define sec second
    typedef std::pair<int, int> pii;
    typedef std::vector<int> veci;
    typedef std::vector<pii> vecpii;
    typedef long long ll;
    ll Abs(ll x) { return x < 0 ? -x : x; }
    template <typename T>
    T& read(T& r) {
    	r = 0; bool w = 0; char ch = getchar();
    	while(ch < '0' || ch > '9') w = ch == '-' ? 1 : 0, ch = getchar();
    	while(ch >= '0' && ch <= '9') r = r * 10 + (ch ^ 48), ch = getchar();
    	return r = w ? -r : r;
    }
    inline int lowbit(int x) { return x & (-x); }
    const int N = 2000100;
    int n, a[N], pos[N];
    struct BIT {
    	int tree[N];
    	int sumq(int x) { int sum = 0; for(; x; x -= lowbit(x)) sum += tree[x]; return sum; }
    	void modify(int x, int v) { for(; x <= n; x += lowbit(x)) tree[x] += v; }
    }tr;
    ll ans[N], c[N];
    std::set<int>st;
    ll calc(ll x) { return x * (x + 1) / 2; } 
    signed main() {
    	read(n);
    	for(int i = 1; i <= n; ++i) read(a[i]), pos[a[i]] = i;
    	for(int i = n; i; --i) {
    		c[a[i]] = tr.sumq(a[i]);
    		tr.modify(a[i], 1);
    	}
    	int p = 1, num = 1; st.insert(pos[1]);
    	for(int i = 2; i <= n; ++i) {
    		ans[i] = ans[i-1] + c[i];
    		if(!(i&1)) {
    			ans[i] += Abs(pos[num] - pos[i]);
    			if(pos[i] < pos[num]) ++p;
    			st.insert(pos[i]);
    			continue ;
    		}
    		st.insert(pos[i]);
    		if(pos[i] < pos[num]) ++p;
    		if(p != (i+1)/2) {
    			auto it = st.lower_bound(pos[num]);
    			if(pos[i] < pos[num]) --it, --p;
    			else ++it, ++p;
    			num = a[*it];
    		}
    		ans[i] += Abs(pos[num] - pos[i]);
    	}
    	for(int i = 1; i <= n; ++i) printf("%lld ", ans[i] - calc((i-1)/2) - calc(i/2));
    	return 0;
    }
    
  • 相关阅读:
    qt自定义的串口类判断断开
    ubuntu16.04永久修改有线接口名称(enp0s3->eth0)
    记录一下读过的书
    Qt 主界面卡死
    Mysql5.7及版本以上导入sql提示Incorrect date value: '0000-00-00' for column
    webpack打包css
    ant-design-vue中的a-directory-tree更换图标
    解决php-fpm占用内存过高问题
    centos,解压源代码安装,没有configure文件
    OSS存储上遇到The difference between the request time and the current time is too large
  • 原文地址:https://www.cnblogs.com/do-while-true/p/15321783.html
Copyright © 2011-2022 走看看