zoukankan      html  css  js  c++  java
  • CSP2019 树上的数

    题目链接

    Solution

    以下是各位大佬们总结出来的正确的考场拿分顺序

    10pts 暴力

    (这不就是我在考场上写的吗 QWQ

    暴搜枚举删边顺序,复杂度 (O(n!))

    Code

    附上考场代码

    #include <iostream>
    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    #include <cmath>
    using namespace std;
    
    const int N = 433333;
    int n, k, T, w[N], ans[N], e[N], f[N], vis[N], tre[N];
    
    bool lit()
    {
        for(int i = 1; i <= n; i++)
            if(w[i] != ans[i]) return w[i] < ans[i];
        return false;
    }
    
    void dfs(int cnt)
    {
        if(cnt == n - 1)
        {
            if(lit())
                for(int i = 1; i <= n; i++) ans[i] = w[i];
            return ;
        }
        for(int i = 1; i < n; i++)
        {
            if(vis[i]) continue;
            swap(w[tre[e[i]]], w[tre[f[i]]]);
            swap(tre[e[i]], tre[f[i]]);
            vis[i] = 1;
            dfs(cnt + 1);
            vis[i] = 0;
            swap(w[tre[e[i]]], w[tre[f[i]]]);
            swap(tre[e[i]], tre[f[i]]);
        }
    }
    
    int main()
    {
        //freopen("tree.in", "r", stdin);
        //freopen("tree.out", "w", stdout);
        scanf("%d", &T);
        while(T--)
        {
            scanf("%d", &n);
            memset(vis, 0, sizeof(vis));
            memset(ans, 0x3f, sizeof(ans));
            memset(w, 0, sizeof(w));
            memset(tre, 0, sizeof(tre));
            memset(e, 0, sizeof(e));
            memset(f, 0, sizeof(f));
            for(int i = 1; i <= n; i++) scanf("%d", &w[i]);
            for(int i = 1; i <= n; i++) tre[w[i]] = i;
            for(int i = 1; i < n; i++)
                scanf("%d%d", &e[i], &f[i]);
            dfs(0);
            for(int i = 1; i <= n; i++) printf("%d ", ans[i]);
            printf("
    ");
        }
        //fclose(stdin);
        //fclose(stdout);
        return 0;
    }
    

    35pts 菊花图

    假设交换顺序是 {(p_{rt}, p_1, p_2, p_3...p_{n-1})},经过手模可以发现,最后每个点的权值都跑到下一个点的位置上去了。就是 (a_{rt}) -> (p_1) , (a_{p_1}) -> (p_2) , (a_{p_2}) -> (p_3...a_{p_{n-1}}) -> (p_{rt})

    于是我们得出一个性质:对于每个点,让它与它最后的位置连边,最后会形成一个大环。既然是一个大环,那么中间形成小环的情况就不合法。因此只需要贪心地连边,用并查集维护。

    Code

    代码仅是菊花图。

    #include <iostream>
    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    using namespace std;
    
    const int N = 2333333;
    int T, rt, n, p[N], a[N], du[N], fa[N], vis[N], ans[N];
    
    int find(int x) { return x == fa[x] ? x : fa[x] = find(fa[x]); }
    
    int main()
    {
        scanf("%d", &T);
        while(T--)
        {
            scanf("%d", &n);
            memset(fa, 0, sizeof(fa));
            memset(du, 0, sizeof(du));
            memset(vis, 0, sizeof(vis));
            memset(ans, 0, sizeof(ans));
            for(int i = 1; i <= n; i++) scanf("%d", &p[i]), a[p[i]] = i;
            for(int u, v, i = 1; i < n; i++) scanf("%d%d", &u, &v), du[u]++, du[v]++;
            for(int i = 1; i <= n; i++) fa[i] = i; //并查集初始化 
            for(int i = 1; i <= n; i++) if(du[i] > 1) rt = a[i]; //找到菊花图的根节点 
            for(int i = 1; i <= n; i++)
                for(int j = 1; j <= n; j++)
                    if(!vis[j] && find(p[i]) != find(j)) //找当前权值能到的最小的点 
                    {
                        fa[find(p[i])] = find(j);
                        ans[i] = j, vis[j] = 1;
                        break;
                    }
            for(int i = 1; i < n; i++) printf("%d ", ans[i]);
            for(int i = 1; i <= n; i++) if(!vis[i]) printf("%d
    ", i);
        }
        return 0;
    }
    

    60pts 链

    (开局一张图,内容全靠编)

    假设图中的 (a_u)向右跑到图中的 (v) 点去,我们可以得到以下几条性质:

    (1.) (u) 点的右边要在左边删之前删,要不然 (u) 点早就跑到左边了。

    (2.) (u)(v) 之间的点都是先删左边再删右边

    (2.) (v) 点右边的边要在左边的边删之前删,要不然好不容易跑到 (v) 点的 (a_u) 一会儿就会继续向右跑

    同样的道理,(a_v) 向左跑到 (u) 也可以轻松地推出来。

    抽象一下这个东西:给每个点打一个 tag,tag = 0 表示未标记;tag = 1 表示先删左边再删右边;tag = 2 表示先删右边再删左边

    每次只要贪心地去找,让较小的权值尽量到达编号较小的点。删边过程中添加的标记不能与已有的标记冲突。

    注意: 链开头的点和结尾的点没有标记

    Code

    代码仅是链。

    #include <iostream>
    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    using namespace std;
    
    const int N = 6666;
    struct edge { int nxt, to; } e[N];
    int T, n, st, tot, cnt, l[N], p[N], a[N], du[N], vis[N];
    int ans[N], tag[N], use[N], head[N];
    
    void add(int x, int y)
    {
        e[++cnt] = (edge) { head[x], y };
        head[x] = cnt;
    }
    
    int main()
    {
        scanf("%d", &T);
        while(T--)
        {
            scanf("%d", &n);
            tot = cnt = st = 0;
            memset(p, 0, sizeof(p));
            memset(l, 0, sizeof(l));
            memset(du, 0, sizeof(du));
            memset(use, 0, sizeof(use));
            memset(vis, 0, sizeof(vis));
            memset(ans, 0, sizeof(ans));
            memset(head, 0, sizeof(head));
            for(int i = 1; i <= n; i++) scanf("%d", &p[i]);
            for(int u, v, i = 1; i < n; i++)
            {
                scanf("%d%d", &u, &v);
                add(u, v), add(v, u);
                du[u]++, du[v]++;
            }
            for(int i = 1; i <= n; i++) if(du[i] == 1) { st = i; break; }
            // 找到这条链 
            while(st)
            {
                int fl = 0; 
                l[++tot] = st, vis[st] = 1, use[st] = tot;
                for(int i = head[st]; i; i = e[i].nxt)
                {
                    int v = e[i].to;
                    if(!vis[v])
                    {
                        st = v, fl = 1;
                        break;
                    }
                }
                if(fl == 0) break;
            }
            // 开始贪心统计答案 
            memset(tag, 0, sizeof(tag));
            memset(vis, 0, sizeof(vis));
            for(int i = 1; i <= n; i++)
            {
                int res = 0x3f3f3f3f, ki = 0;
                // 往右边走 
                if(!tag[p[i]] || tag[p[i]] == 2)
                {
                    for(int j = use[p[i]] + 1; j <= n; j++)
                    {
                        if(!tag[l[j - 1]] || tag[l[j - 1]] == 1 || l[j - 1] == p[i])
                        {
                            if((!tag[l[j]] || tag[l[j]] == 2) && !vis[l[j]])
                                if(res > l[j]) res = l[j], ki = 1;
                        }
                        else break;
                    }
                }
                // 往左边走 
                if(!tag[p[i]] || tag[p[i]] == 1)
                {
                    for(int j = use[p[i]] - 1; j >= 1; j--)
                    {
                        if(!tag[l[j + 1]] || tag[l[j + 1]] == 2 || l[j + 1] == p[i])
                        {
                            if((!tag[l[j]] || tag[l[j]] == 1) && !vis[l[j]])
                                if(res > l[j]) res = l[j], ki = 2;
                        }
                        else break;
                    }
                }
                // 如果往右边走更优 
                if(ki == 1)
                {
                    tag[p[i]] = 2, tag[res] = 2;
                    for(int j = use[p[i]] + 1; l[j] != res; j++) tag[l[j]] = 1;
                }
                // 如果往左边走更优 
                if(ki == 2)
                {
                    tag[p[i]] = 1, tag[res] = 1;
                    for(int j = use[p[i]] - 1; l[j] != res; j--) tag[l[j]] = 2;
                }
                tag[l[1]] = tag[l[n]] = 0, ans[i] = res;
                vis[res] = 1;
            }
            for(int i = 1; i <= n; i++) printf("%d ", ans[i]);
            printf("
    ");
        }
        return 0;
    }
    

    100pts 正解

    不会

  • 相关阅读:
    js代码与html代码分离示例
    day24_Nginx学习笔记
    bookStore商城开发文档
    API Management Architecture Notes
    Taking A Fresh Look At What Open Source API Management Architecture Is Available
    使用API Gateway
    Qcon2017实录|Service Mesh:下一代微服务
    聊聊 API Gateway 和 Netflix Zuul
    项目长期运维中产生的一些问题
    忆情天书的由来
  • 原文地址:https://www.cnblogs.com/Andy-park/p/13778312.html
Copyright © 2011-2022 走看看