zoukankan      html  css  js  c++  java
  • Codeforces Round #621 (Div. 1 + Div. 2)

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

    A - Cow and Haybales

    一开始读错了题,看见t神0分钟过了才知道确实是签到。

    题意:给一个非负整数序列,每次操作可以把一个非零数字-1,相邻的数字+1。求至多能使得第1个数字变得多大?

    题解:贪心,尽量从最近的数字往第1个数字搬。事实上因为数据量极小,每次搬1确实也可以。

    B - Cow and Friend

    题意:给一个非负整数序列,每次操作先选一个数字,然后在二维平面上走恰好这个数字长度,求最少要多少次操作才能从(0,0)到(x,0)。

    题解:首先假如有个与x相等的数字,那么答案就是1。其次,假如最大的数字maxa>=x/2,那么可以走一个等腰三角形到达(在完全伸开到达2*max和完全折叠到达0之间必有一点恰好等于x),答案是2。那么得到一个理所当然的贪心,就是先往x走到尽可能近,然后假如某一步走到超过了x,就把最后的两步折起来变成一个等腰三角形。

    C - Cow and Message

    题意:给一个字符串,求其中出现次数最多的子序列的出现次数。

    题解:当时猜了一个结论,就是一定是两个不同的字母的组合成的长度为2的序列,由于这样的组合本身就26*25种,所以可以直接莽,假如枚举的是"ab",那么记录"a"的数量,然后每次遇到"b"就把这个前面的"a"的数量加上去。然后相了一想,假如是"aaaaa",答案对应的序列就应该是"aa",然后在cnta*(cnta+1)/2和cnta*(cnta-1)/2之间纠结,最后从"aaa"中确定是后者。准备提交的时候想到,假如只有1个字符,那我的答案岂不是0?还好立刻补了一发。其实假如把初始值设为1就没有这个问题了。最后发现其实两个相同的字母的统计方法也可以包含在第1种情况中。

    感觉应该会有人因为只有1个字母的数据挂掉。确实,main test的36就是这个东西,有几个紫名爷掉了。

    当时hack了一个用int的人,没想到被

    #define int long long
    

    制裁了。

    char s[100005];
    int cnt[128];
    void test_case() {
        scanf("%s", s + 1);
        int n = strlen(s + 1);
        for(int i = 1; i <= n; ++i)
            ++cnt[s[i]];
        ll ans = 0;
        for(int i = 'a'; i <= 'z'; ++i)
            ans = max(ans, 1ll * cnt[i]);
        for(int i = 'a'; i <= 'z'; ++i)
            ans = max(ans, 1ll * cnt[i] * (cnt[i] - 1ll) / 2ll);
        for(int i = 'a'; i <= 'z'; ++i) {
            for(int j = 'a'; j <= 'z'; ++j) {
                if(j == i)
                    continue;
                if(1ll * cnt[i]*cnt[j] <= ans)
                    continue;
                int cnti = 0;
                ll sum = 0;
                for(int k = 1; k <= n; ++k) {
                    if(s[k] == i)
                        ++cnti;
                    else if(s[k] == j)
                        sum += cnti;
                }
                ans = max(ans, sum);
            }
        }
        printf("%lld
    ", ans);
    }
    

    *D - Cow and Fields

    第一次在比赛过程中能过标签带graph的1900(猜)的题耶!

    题意:给n个点,m条边的无向连通图,每天要从1号点走到n号点。给出k个特殊点,要求在这些特殊点之间加入恰好1条边,使得整个图的从1到n的最短路尽可能长,求出这个最短路的长度。

    题解:首先有一个很显然但是没什么卵用的观察,就是假如特殊点之间有边,那么加一条重边就是答案。这种题好像做过几次有点套路了?还是说变强了?当时猜测是要按dis1[x]的大小分层考虑,每一层的x都向同层或者深层的y连边,这样的最短路就保证为dis1[x]+1+disn[y],但是不知道怎么证明。今天补上这个证明。

    设这两个特殊点为x,y,考虑必须走新加的边的最短路,那么这个最短路的长度必定为Lxy=min(dis1[x]+1+disn[y],dis1[y]+1+disn[x]),然后从所有的x,y的组合中选出最大的Lxy,与原本的最短路取min,就是题目要找的答案。麻烦的地方在于一开始的这个min,毕竟这条边是有两种走法的,然后隐隐约约感觉到,貌似可以通过额外的信息确定这条边要按哪个方向走(才可能形成最短路)。

    虽然要去掉这个min要分很多种情况,但是不失一般性,可以先规定dis1[x]<=dis1[y],然后分disn[x]<disn[y],disn[x]=disn[y],disn[x]>disn[y]三种情况讨论。

    先考虑最简单的disn[x]=disn[y],此时直接提取到外面Lxy=disn[y]+1min(dis1[x],dis1[y])=dis1[x]+1+disn[y]。要取这个的最大值,只需要按dis1[x]排序满足dis1[x]<=dis1[y]后,使用一个后缀最大值即可。

    可能存在某些点,满足dis1[x]<=dis1[y],同时disn[x]<disn[y],例如一个T形的,1号点和n号点在两端,x在中间的交叉处,y在下面。这个时候的最短路不可能比dis1[x]+disn[x]=dis1[n]更短,直接忽略,所以把不优的答案dis1[x]+1+disn[y]也合并进来并不会产生不良影响

    可能存在某些点,满足dis1[x]<=dis1[y],同时disn[x]>disn[y],比如x和y都在原本的从1号点到n号点的最短路上,就是满足这样的式子,这种情况最小值确实就是dis1[x]+1+disn[y],因为它(有可能)缩短了最短路,而且从dis1[x]+1+disn[y]<dis1[y]+1+disn[x]也可以知道直接合并dis1[x]+1+disn[y]即可。

    综上所述,按dis1[x]排序,然后取disn[y]的后缀最大值。

    vector<int> G[200005];
    int dis1[200005];
    int disn[200005];
    bool vis[200005];
    int *dis;
     
    priority_queue<pair<int, int> > pq;
    void dijkstra(int s, int n) {
        while(!pq.empty())
            pq.pop();
        for(int i = 1; i <= n; ++i) {
            dis[i] = 0x3f3f3f3f;
            vis[i] = 0;
        }
        dis[s] = 0;
        pq.push({-dis[s], s});
        while(!pq.empty()) {
            int u = pq.top().second;
            pq.pop();
            if(vis[u])
                continue;
            vis[u] = 1;
            for(int i = 0; i < G[u].size(); ++i) {
                int v = G[u][i];
                if(!vis[v] && dis[v] > dis[u] + 1) {
                    dis[v] = dis[u] + 1;
                    pq.push({-dis[v], v});
                }
            }
        }
    }
     
    int a[200005];
     
    pii PII[200005];
    int sufmax[200005];
     
    void test_case() {
        int n, m, k;
        scanf("%d%d%d", &n, &m, &k);
        for(int i = 1; i <= k; ++i)
            scanf("%d", &a[i]);
        while(m--) {
            int u, v;
            scanf("%d%d", &u, &v);
            G[u].push_back(v);
            G[v].push_back(u);
        }
        dis = dis1;
        dijkstra(1, n);
        dis = disn;
        dijkstra(n, n);
        for(int i = 1; i <= k; ++i)
            PII[i] = {dis1[a[i]], a[i]};
        sort(PII + 1, PII + 1 + k);
        sufmax[k] = disn[PII[k].second];
        for(int i = k - 1; i >= 1; --i)
            sufmax[i] = max(sufmax[i + 1], disn[PII[i].second]);
        /*for(int i = 1; i <= k; ++i)
            printf("%d %d
    ", PII[i].first, PII[i].second);
        for(int i = 1; i <= k; ++i)
            printf("%d
    ", sufmax[i]);*/
        int ans = disn[1], maxans = 0;
        for(int i = 1; i < k; ++i) {
            int u = PII[i].second;
            int tmp = dis1[PII[i].second] + 1 + sufmax[i + 1];
            if(tmp >= ans) {
                printf("%d
    ", ans);
                return;
            }
            maxans = max(maxans, tmp);
        }
        printf("%d
    ", maxans);
    }
    

    反思:最短路其实可以用bfs的,结合桶排序或者其他什么DAG上dp可以把复杂度确实降低到线性。

    收获:貌似最短路的变化一般都从最短路径生成树(以及dis[v]=dis[u]+w的点也是一条最短路),以及根据距离分层的算法来考虑?

  • 相关阅读:
    Server responded "Algorithm negotiation failed" SSH Secure链接服务器错误
    简单实现elementui的el-container布满全屏
    安装apue.h
    编译原理
    点亮一盏灯
    什么是Complement(补码)?(转)
    轻松学C语言
    求二进制表示
    gst-launch-1.0 (1)
    使用docker-compose 大杀器来部署服务 上 (转)
  • 原文地址:https://www.cnblogs.com/KisekiPurin2019/p/12324879.html
Copyright © 2011-2022 走看看