Codeforces Global Round 3

时间:2020-03-26
本文章向大家介绍Codeforces Global Round 3,主要包括Codeforces Global Round 3使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。

题目链接:https://codeforces.com/contest/1148

当年我不会做这个B题,却把CD过了。这个E题想错了。

A - Another One Bites The Dust

题意:有x个"a",y个"b",z个"ab",求最长的一个连续两个字符都不相同的字符串。

题解:肯定是先把z全部用了,易知'a'的数量和'b'的数量的相差不能超过1。

*B - Born This Way

这个题感觉不简单,没想到是1700难度的。

题意:要从A坐飞机中转B再到达C。A有n种航班飞到B,起飞时间分别是ai,飞行时间都是ta。B有m种航班飞到C,起飞时间分别是bj,飞行时间都是tb。取消不超过k种航班,使得最早的到达C的时间最迟。

题解:一开始想着贪心,但是怎么知道要使得这个B航班失效,是直接把他取消还是把能到他的A航班都取消呢?这个不能贪心。但是观察一段时间之后会发现,A航班总是取消最早的连续x种是最好的,因为假如也是取消x种,假如不是最早的连续x种,那么后面的就白白浪费取消的次数。然后又可以惊人的发现,枚举x之后,再取消第一个没有被取消的A航班能接上的接下来的k-x种B航班,就是最优的。也就是说是贪心取消掉最早的A航班,然后贪心取消掉剩下的有用的B航班里面最早的。

枚举这个x,然后双指针推一下。

不要溢出。

ll a[200005];
ll b[200005];
 
void TestCase() {
    int n, m, k;
    ll ta, tb;
    scanf("%d%d%lld%lld%d", &n, &m, &ta, &tb, &k);
    for(int i = 1; i <= n; ++i)
        scanf("%lld", &a[i]);
    for(int j = 1; j <= m; ++j)
        scanf("%lld", &b[j]);
    if(k >= n || k >= m) {
        puts("-1");
        return;
    }
    ll ans = 0;
    int curj = 1;
    //由于下面是枚举取消A航班的数量,所以上界是k+1,对应全部取消,易知a[k+1]一定存在
    for(int i = 1; i <= k + 1; ++i) {
        //取消掉前面的前i-1种A航班
        while(curj <= m && a[i] + ta > b[curj]) {
            //双指针找到第i种A航班能使用的第1个B航班,记为curj
            ++curj;
        }
        int t = k - (i - 1);
        //若剩余的B航班可以全部取消,输出-1
        if((m - curj + 1) <= t) {
            puts("-1");
            return;
        }
        //取消[curj,curj+t-1]的B航班
        ans = max(ans, b[curj + t] + tb);
    }
    printf("%lld\n", ans);
 
}

C - Crazy Diamond

这个题也比较有想法。

题意:给一个n个数的排列,n是偶数,使用不超过5n次交换,给这个数组排序,要求每次交换的时候,选择的两个下标(i,j),满足2|i-j|>=n,换言之就是两个要隔得足够远,至少要隔半个数列的长度。

题解:这样看见“半个数列的长度”,就想到可以充分利用第1个元素和第n个元素来搞事,易知前半区间[1,n/2]的都可以和第n个元素交换,后半区间[n/2+1,n]的都可以和第1个元素交换。

先写一个交换函数,这样就不怕搞错了。

void myswap(int x, int y) {
    swap(a[x], a[y]);
    swap(pos[a[x]], pos[a[y]]);
    ans[++atop] = {x, y};
}

每次调整第i个元素的时候,都满足前i-1个元素复位。

那么设数字i的位置为x,若2|x-i|>=n,就可以直接交换。

否则就要借助一些外力了。

以n=8为例:

1、若i<=n/2,且x>n/2:

由于他们离得比较近,所以i不会是1,x也不会是n

1.i.x..n

先交换(x,1)

x.i.1..n

再交换(1,n)

n.i.1..x

再交换(i,n)

n.x.1..i

再交换(x,1)

1.x.n..i

这样就完成了任务,至于n和i是怎么样的,就留给后面的迭代自己处理。

2、若i<=n/2,且x<=n/2:

1.ix...n

先交换(i,n)

1.nx...i

再交换(x,n)

1.ni...x

再交换(i,n)

1.xi...n

又完成了任务。

否则,两者都必定在右半区间,把上面的第2种情况反过来就可。

int n;
int a[300005];
int pos[300005];
 
pii ans[1500005];
int atop;
 
void myswap(int x, int y) {
    swap(a[x], a[y]);
    swap(pos[a[x]], pos[a[y]]);
    ans[++atop] = {x, y};
}
 
void solve(int i) {
    if(a[i] == i)
        return;
    int x = pos[i];
    if(2 * (x - i) >= n) {
        myswap(x, i);
        return;
    }
    if(i <= n / 2) {
        if(x > n / 2) {
            myswap(x, 1);
            myswap(1, n);
            myswap(n, i);
            myswap(x, 1);
            return;
        } else {
            myswap(i, n);
            myswap(x, n);
            myswap(i, n);
            return;
        }
    } else {
        myswap(i, 1);
        myswap(x, 1);
        myswap(i, 1);
        return;
    }
}
 
void TestCase() {
    scanf("%d", &n);
    for(int i = 1; i <= n; ++i) {
        scanf("%d", &a[i]);
        pos[a[i]] = i;
    }
    atop = 0;
    for(int i = 1; i <= n; ++i)
        solve(i);
    printf("%d\n", atop);
    for(int i = 1; i <= atop; ++i)
        printf("%d %d\n", ans[i].first, ans[i].second);
    return;
}

原文地址:https://www.cnblogs.com/KisekiPurin2019/p/12572742.html