zoukankan      html  css  js  c++  java
  • 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
    ", 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
    ", atop);
        for(int i = 1; i <= atop; ++i)
            printf("%d %d
    ", ans[i].first, ans[i].second);
        return;
    }
    

    *D - Dirty Deeds Done Dirt Cheap

    这道题这么难,我当年居然会?

    题意:给n对数字<ai,bi>,每个数字都是不同的,且正好是[1,2n],选出最长的一个集合,使得他们可以排列成一个“好串”。一个“好串”,是符号为"a1a2a3a4<b4"或者"a1>b1b2b3b4"的串,也就是这些符号是反过来的。

    题解:可以确定每对数字的符号是'<'还是'>',显然不同种符号是不可以接在一起的。那么就先按内部符号分成两类,然后我惊人地发现,这些可以全部排在一起!比如都是'<'号的,中间都必须用'>'号连接,那么假如把这些按右边的数字bi的大小从大到小排序,那么b1>b2,而a2<b2,所以一定是能接上的,对称的情况同理。

    struct Node {
        int id;
        int l, r;
    } nd[300005];
     
    pii p1[300005],p2[300005];
     
    void TestCase() {
        int n;
        scanf("%d", &n);
        for(int i = 1; i <= n; ++i) {
            nd[i].id = i;
            scanf("%d%d", &nd[i].l, &nd[i].r);
        }
        int m1 = 0, m2 = 0;
        for(int i = 1; i <= n; ++i) {
            if(nd[i].l < nd[i].r) {
                ++m1;
                p1[m1].first = -nd[i].r;
                p1[m1].second = nd[i].id;
            } else {
                ++m2;
                p2[m2].first = nd[i].r;
                p2[m2].second = nd[i].id;
            }
        }
        if(m1 >= m2) {
            sort(p1 + 1, p1 + 1 + m1);
            printf("%d
    ", m1);
            for(int i = 1; i <= m1; ++i)
                printf("%d%c", p1[i].second, " 
    "[i == m1]);
        }
        else{
            sort(p2 + 1, p2 + 1 + m2);
            printf("%d
    ", m2);
            for(int i = 1; i <= m2; ++i)
                printf("%d%c", p2[i].second, " 
    "[i == m2]);
        }
    }
    

    *E - Earth Wind and Fire

    题意:给n个石头,每次可以选两个石头,让他们彼此靠近一定距离,准确的说,选择两个(i,j),下标为pi,pj且pi<pj,然后选择一个d满足0<=2d<=pj-pi,再换成pi+d和pj-d,也就是挨近了一点。要求使用不超过5n次操作从石头序列s构造出石头序列t。注意石头序列t是无序的。

    题解:注意到操作不会改变所有元素的和,所以要是s序列的和与t序列的和不一样就是NO。然后假如某次t序列的无法构造,也是NO。一开始觉得是每次取s序列两端的出来向中间靠,直到碰到第一个t序列石头t为止,这样每次可以至少消除一个t序列的石头。

    写出来是这样的:

    map<int, vector<int> > MAP;
    deque<int> DEQUE;
     
    int t[300005];
    struct Ans {
        int i, j, d;
        Ans(int i = 0, int j = 0, int d = 0): i(i), j(j), d(d) {}
    } ans[300005];
    int atop;
     
    void TestCase() {
        int n;
        scanf("%d", &n);
        ll sum1 = 0;
        for(int i = 1; i <= n; ++i) {
            int x;
            scanf("%d", &x);
            MAP[x].push_back(i);
            sum1 += x;
        }
        ll sum2 = 0;
        for(int i = 1; i <= n; ++i) {
            scanf("%d", &t[i]);
            sum2 += t[i];
        }
        if(sum1 != sum2) {
            puts("NO");
            return;
        }
        sort(t + 1, t + 1 + n);
        for(int i = 1; i <= n; ++i)
            DEQUE.push_back(t[i]);
        while(DEQUE.size()) {
            int pos1 = MAP.begin()->first;
            int id1 = MAP.begin()->second.back();
     
            int ypos1 = DEQUE.front();
     
            if(pos1 == ypos1) {
                MAP.begin()->second.pop_back();
                DEQUE.pop_front();
                if(MAP.begin()->second.empty())
                    MAP.erase(pos1);
                continue;
            }
     
            int pos2 = MAP.rbegin()->first;
            int id2 = MAP.rbegin()->second.back();
     
            int ypos2 = DEQUE.back();
     
            if(pos2 == ypos2) {
                MAP.rbegin()->second.pop_back();
                DEQUE.pop_back();
                if(MAP.rbegin()->second.empty())
                    MAP.erase(pos2);
                continue;
            }
     
            MAP.begin()->second.pop_back();
            MAP.rbegin()->second.pop_back();
     
            if(MAP.begin()->second.empty())
                MAP.erase(pos1);
            if(MAP.rbegin()->second.empty())
                MAP.erase(pos2);
     
            if(pos1 > ypos1 || pos2 < ypos2) {
                puts("NO");
                return;
            }
            int dis = min(ypos1 - pos1, pos2 - ypos2);
            pos1 += dis;
            pos2 -= dis;
            if(pos1 == ypos1) {
                DEQUE.pop_front();
                if(pos2 == ypos2)
                    DEQUE.pop_back();
                else
                    MAP[pos2].push_back(id2);
            } else {
                assert(pos2 == ypos2);
                DEQUE.pop_back();
                MAP[pos1].push_back(id1);
            }
            ans[++atop] = Ans(id1, id2, dis);
        }
        puts("YES");
        printf("%d
    ", atop);
        for(int i = 1; i <= atop; ++i)
            printf("%d %d %d
    ", ans[i].i, ans[i].j, ans[i].d);
        return;
    }
    

    但是这样是错的,比如下面的例子:

    4
    1 3 5 7
    2 2 6 6
    

    假如按照上面的算法,会先变成(1,7)->(2,6):

    2 3 5 6
    

    然后报告NO。

    但是实际上可以用(1,3)->(2,2),(5,7)->(6,6)。

    另一种思路是,先给s序列和t序列排序,那么他们就是对应的一一对应的移动关系,这样每个石头有可能是需要左移,也有可能是需要右移。但是第一个石头绝对是要右移,那么就找到第一个左移的石头,移动min(石头2左移的距离,石头1右移的距离),这样至少会把一个石头归位。易知需要左移的总和和需要右移的总和是相等的,所以是一定可以构造的。

    pii s[300005];
    int t[300005];
    
    struct Ans {
        int i, j, d;
        Ans(int i = 0, int j = 0, int d = 0): i(i), j(j), d(d) {}
    } ans[300005];
    int atop;
    
    void AddAns(int i, int j, int d) {
        s[i].first += d;
        s[j].first -= d;
        ans[++atop] = Ans(s[i].second, s[j].second, d);
    }
    
    void TestCase() {
        int n;
        scanf("%d", &n);
        ll sums = 0;
        for(int i = 1; i <= n; ++i) {
            scanf("%d", &s[i].first);
            s[i].second = i;
            sums += s[i].first;
        }
        ll sumt = 0;
        for(int i = 1; i <= n; ++i) {
            scanf("%d", &t[i]);
            sumt += t[i];
        }
        if(sums != sumt) {
            puts("NO");
            return;
        }
        sort(s + 1, s + 1 + n);
        sort(t + 1, t + 1 + n);
        atop = 0;
        for(int i = 1, j = 1; i <= n; ++i) {
            if(s[i].first > t[i]) {
                puts("NO");
                return;
            }
            if(s[i].first == t[i])
                continue;
            while(s[i].first < t[i]) {
                int d1 = t[i] - s[i].first;
                j = max(j, i + 1);
                while(j <= n && s[j].first <= t[j])
                    ++j;
                if(j > n) {
                    puts("NO");
                    return;
                }
                int d2 = s[j].first - t[j];
                AddAns(i, j, min(d1, d2));
            }
        }
        puts("YES");
        printf("%d
    ", atop);
        for(int i = 1; i <= atop; ++i)
            printf("%d %d %d
    ", ans[i].i, ans[i].j, ans[i].d);
    }
    
  • 相关阅读:
    网页内容切换效果实现的15个jQuery插件
    【转】C#获取客户端及服务器端主机信息及其获取IP地址
    EnableViewState 属性
    Dictionary字典类使用范例
    AspNetPager分页控件官方网站
    [区别]APPlication,Session,Cookie,ViewState和Cache
    C#特性之数据类型
    WindowsPhone8.1 开发技巧
    关于在WP8.1中使用剪贴板的问题
    MVC中使用JQuery方式进行异步请求和使用自带方式进行异步请求
  • 原文地址:https://www.cnblogs.com/KisekiPurin2019/p/12572742.html
Copyright © 2011-2022 走看看