• CF819B Mister B and PR Shifts


    解题报告

    题目

    传送门:https://www.luogu.com.cn/problem/CF819B

    题意翻译

    • 定义一个全排列 p~i~ 的偏移值为(sum_{i=1}^n)| p~i~ - i |
    • 给你一个全排列,你可以从后面拿k∈[0,n−1]个数放在前面,使得该全排列的偏移值最小,输出这个偏移值和k,如果有多个k任意输出一个
    • n ≤ 10^6^

    开整

    (不看翻译是真的看不懂……)

    上来一看以为要用差分,莽一波欢乐报零。于是换个思路想一想。

    就暴力而言,我们是从后往前,一个一个拿到最前面,然后再扫一遍判断ans的变化。

    于是乎,转念一想,我们当前位的数的本身的大小是不变的,而我们每操作一次它的编号 i 就会增大1 。

    那么,对于原先偏移值(注意这个“原”是对于当前次操作而言,而并非是最开始的那个,下面的也是一样,我们的偏移值是在随着操作不断改变的)为正数的,显然对ans的贡献量会减少1,即ans一共会减少原偏移值为正的数的个数。

    对于原偏移值为非正的,显然对ans的贡献会加1,所以ans的增加量是原偏移值为非正数的数的个数。

    所以,我们要维护的就是原偏移值为的和非正的数的个数。

    然后我们就可以考虑了,经过一次操作,原偏移值为+1的数偏移值都会变为0,也就是说,原偏移值为正数的数的个数会少掉原偏移值为+1的数的个数。

    而原偏移值为非正数的数的个数会加上原偏移值为+1的数的个数,这也是显然的。所以原偏移值为非正的数的个数不会变少,只会越来越多。

    另外,每次操作我们把第 n-i+1 位的数放到最前面,这个数所带来的偏移值的改变直接计算即可。

    这样我们整个时间效率应该是稳定的O(2n),然后这题最好别用cin,cout...虽然能过,但是我加优化都跑了15s...

    然后,就这样。

    #include <cstdio>
    #include <algorithm>
    #include <cstring>
    #include <iostream>
    #include <cmath>
    #define ll long long
    using namespace std;
    char buf[1 << 20], *p1 = buf, *p2 = buf;
    char getc() {
    	if(p1 == p2) {
    		p1 = buf;
    		p2 = buf + fread(buf, 1, 1 << 20, stdin);
    		if(p1 == p2) return EOF;
    	}
    	return *p1++;
    }
    inline int read() {
    	int s = 0, w = 1;
    	char c = getc();
    	while(c < '0' || c > '9') {if(c == '-')w = -1; c = getc();}
    	while(c >= '0' && c <= '9') s = s * 10 + c - '0', c = getc();
    	return s * w;
    }
    const int maxn = 3e6 + 10;
    int a[maxn];
    ll ans = 0, k = 0;
    ll zheng_cnt, fu_cnt;
    ll one[maxn];//第i次操作时原偏移值为1的数的个数就是one[i],大家想一想就知道了。
    int main(){
        int n = read();
        for(int i = 1; i <= n; i++) {
            a[i] = read();
            int x = a[i] - i;
            if(x <= 0) {
                fu_cnt++;
                ans += abs(x);
            }
            else {
                zheng_cnt++;
                ans += x;
                one[x]++;
            }
        }
        ll tmp, tmpp = ans;//为了避免错误地修改ans,咱们用两个中间变量跑循环
        if(ans == 0) {//一些剪枝优化,但感觉没什么卵用
            printf("0 0
    ");
            return 0;
        }
        for(int i = 1; i < n; i++){//没什么好说的了
            tmp = tmpp;
            tmp -= zheng_cnt;
            tmp += fu_cnt;
            zheng_cnt -= one[i];
            fu_cnt += one[i];
            
            //对这个挪动的数进行操作
            ll x = a[n - i + 1];
            tmp -= n + 1 - x;
            fu_cnt--;
            if(x > 1) {
                one[x - 1 + i]++;
                tmp += x - 1;
                zheng_cnt++;
            }
            else fu_cnt++;
            //操作结束
            
            if(tmp < ans) {
                ans = tmp;
                k = i;
            }
            if(ans == 0) {//还是剪枝,依然是没什么卵用
                printf("0 %lld
    ", k);
                return 0;
            }
            tmpp = tmp;//记得循环一下变量,不然整个过程就不是连续的了
        }
        printf("%lld %lld
    ", ans, k);
        return 0;
    }
    
    
    
  • 相关阅读:
    ES(一): 架构及原理
    Durid(二): 数据集及存储
    Durid(一): 原理架构
    【DataBase】H2 DateBase与项目集成
    【DataBase】H2 DateBase的拓展使用
    【Mybatis】MyBatis之插件开发(十)
    【DataBase】H2 DateBase的简单使用
    【DataBase】Hsqldb与项目集成
    【DataBase】Hsqldb的简单使用
    【Mybatis】MyBatis之Generator自动生成代码(九)
  • 原文地址:https://www.cnblogs.com/Zfio/p/12856355.html
Copyright © 2020-2023  润新知