• [NOIP 2018 Day1] 简要题解


    [题目链接]

             铺设道路 : https://www.luogu.org/problemnew/show/P5019 

             货币系统 : https://www.luogu.org/problemnew/show/P5020

             赛道修建 : https://www.luogu.org/problemnew/show/P5021

    [题解]

           Problem A 铺设道路

           首先 , 我们有一个分治的思路 , 对于一段区间[l , r] , 最优策略为将区间内的所有数都减掉区间中的最小值 , 然后将该问题分成两个子问题 , 分治下去即可 , 时间复杂度 : O(N ^ 2)

           用ST表预处理区间最小值 , 分治时实现O(1)查询 , 时间复杂度优化为 O(NlogN)

           我们还可以使用贪心算法 , 如果第i个数 > 第(i - 1)个数 , 则它们对答案产生了(Hi - Hi-1)的“贡献” , 贡献相加即为答案 , 证明略 , 时间复杂度 : O(N)

           Problem B 货币系统

           首先有一个结论 : 在最优的情况下 , 必然满足m为n的子集 , 这个结论是显然的 

           根据这个结论 , 我们不妨将a数组按升序排序 , 若第(k + 1)个数能由前k个数组合出 , 则删除第(k + 1)个数

           具体实现时可以完全背包 , 时间复杂度 : O(TNW)(取W = 25000)

           Problem C 赛道修建

           要求长度最小的赛道长度尽可能大 , 我们不妨二分答案limit

           对于以u为根节点的一棵子树 , 我们可以记录每一条连接到u的赛道的长度value , 和这条路径的长度u ,若 :

           1. value + w >= limit , 将答案加一 , 单独构成一条赛道       

           2. value + w < limit , 对于这种情况 , 我们将(value + w)插入到一棵平衡树(可以方便使用std :: multimap)

           然后 , 对于平衡树中的每个数w , 我们贪心地将找到最小的v , 使得w + v >= limit , 并在平衡树中删除w和v

           最后我们只要判断路径条数是否 >= m , 即可

           时间复杂度 : O(NlogN ^ 2) , 注意优化常数 , 如二分上界可定为树的直径

    [代码]

            铺设道路 :

            

    // Sprease Table O(NlogN)
    #include<bits/stdc++.h>
    using namespace std;
    #define MAXN 100010
    const int MAXLOG = 20;
    
    int n;
    long long ans;
    int a[MAXN];
    int mn[MAXN][MAXLOG] , loc[MAXN][MAXLOG];
     
    template <typename T> inline void chkmin(T &x , T y) { x = min(x , y); }
    template <typename T> inline void chkmax(T &x , T y) { x = max(x , y); }
    template <typename T> inline void read(T &x)
    {
       T f = 1; x = 0;
       char c = getchar();
       for (; !isdigit(c); c = getchar()) if (c == '-') f = -f;
       for (; isdigit(c); c = getchar()) x = (x << 3) + (x << 1) + c - '0';
       x *= f;
    }
    inline pair<int , int> query(int l , int r)
    {
        int k = (int)((double)log(r - l + 1) / log(2.0));
        int ret1 = min(mn[l][k] , mn[r - (1 << k) + 1][k]);
        int ret2;
        if (mn[l][k] < mn[r - (1 << k) + 1][k]) ret2 = loc[l][k];
        else ret2 = loc[r - (1 << k) + 1][k];
        return make_pair(ret1 , ret2); 
    }
    inline void solve(int l , int r , int k)
    {
        if (l > r) return;
        pair<int , int> tmp = query(l , r);
        ans += tmp.first - k;
        solve(l , tmp.second - 1 , tmp.first);
        solve(tmp.second + 1 , r , tmp.first);    
    }
    
    int main()
    {
        
        read(n);
        for (int i = 1; i <= n; i++) 
        {    
            read(a[i]);
            mn[i][0] = a[i];
            loc[i][0] = i;
        }
        for (int j = 1; j < MAXLOG; j++)
        {
            for (int i = 1; i + (1 << j) - 1 <= n; i++)
            {
                mn[i][j] = min(mn[i][j - 1] , mn[i + (1 << (j - 1))][j - 1]);
                if (mn[i][j - 1] < mn[i + (1 << (j - 1))][j - 1]) loc[i][j] = loc[i][j - 1];
                else loc[i][j] = loc[i + (1 << (j - 1))][j - 1];        
            }
        }
        solve(1 , n , 0);
        cout<< ans << "
    ";
        
        return 0;
    }

                货币系统 :

                 

    #include<bits/stdc++.h>
    using namespace std;
    #define MAXN 110
    
    int n;
    int a[MAXN];
    bool dp[35520];
    
    template <typename T> inline void chkmax(T &x,T y) { x = max(x,y); }
    template <typename T> inline void chkmin(T &x,T y) { x = min(x,y); }
    template <typename T> inline void read(T &x)
    {
        T f = 1; x = 0;
        char c = getchar();
        for (; !isdigit(c); c = getchar()) if (c == '-') f = -f;
        for (; isdigit(c); c = getchar()) x = (x << 3) + (x << 1) + c - '0';
        x *= f;
    }
    int main()
    {
            
            int T;
            read(T);
            while (T--)
            {
                    read(n);
                    for (int i = 1; i <= n; i++) read(a[i]);
                    memset(dp , false , sizeof(dp));
                    dp[0] = true;
                    int ans = 0;
                    sort(a + 1 , a + n + 1);
                    for (int i = 1; i <= n; i++)
                    {
                            if (dp[a[i]]) continue;
                            ++ans;
                            for (int j = a[i]; j <= 25000; j++)
                            {
                                    dp[j] |= dp[j - a[i]];
                            }
                    }                
                    printf("%d
    " , ans);
            } 
            
            return 0;
        
    }

                     赛道修建 :

                      

    #include<bits/stdc++.h>
    using namespace std;
    #define MAXN 50010
    typedef long long ll;
    typedef long double ld;
    const ll inf = 1e15;
    
    struct edge
    {
            int to , w , nxt;
    } e[MAXN << 1];
    
    int n , m , tot , cnt;
    int head[MAXN];
    ll dist[MAXN];
    multiset< ll > M[MAXN];
    
    template <typename T> inline void chkmax(T &x,T y) { x = max(x,y); }
    template <typename T> inline void chkmin(T &x,T y) { x = min(x,y); }
    template <typename T> inline void read(T &x)
    {
        T f = 1; x = 0;
        char c = getchar();
        for (; !isdigit(c); c = getchar()) if (c == '-') f = -f;
        for (; isdigit(c); c = getchar()) x = (x << 3) + (x << 1) + c - '0';
        x *= f;
    }
    inline void addedge(int u , int v , int w)
    {
            ++tot;
            e[tot] = (edge){v , w , head[u]};
            head[u] = tot;
    }
    inline ll dfs(int u , int father , ll limit)
    {
            M[u].clear();
            for (int i = head[u]; i; i = e[i].nxt)
            {
                    int v = e[i].to , w = e[i].w;
                    if (v == father) continue;
                    ll val = dfs(v , u , limit) + (ll)w;
                    if (val >= limit) ++cnt;
                    else M[u].insert(val);
            }    
            ll ret = 0;             
            while (!M[u].empty())
            {
                    if ((int)M[u].size() == 1) 
                    {
                            chkmax(ret , *M[u].begin());
                            break;
                    } else
                    {
                            ll tmp = *M[u].begin();
                            M[u].erase(M[u].begin());
                            multiset< ll > :: iterator it = M[u].lower_bound(limit - tmp);
                            if (it == M[u].end()) 
                            {
                                    chkmax(ret , tmp);
                            } else
                            {
                                    ++cnt;
                                    M[u].erase(it);                            
                            }
                    }
            }
            return ret;
    }
    inline ll diameter()
    {
            queue< int > q;
            for (int i = 1; i <= n; i++) dist[i] = inf;
            dist[1] = 0;
            q.push(1);
            while (!q.empty())
            {
                    int u = q.front();
                    q.pop();
                    for (int i = head[u]; i; i = e[i].nxt)
                    {
                            int v = e[i].to , w = e[i].w;
                            if (dist[u] + w < dist[v])
                            {
                                    dist[v] = dist[u] + w;
                                    q.push(v);
                            }
                    }
            }
            int s = 1;
            for (int i = 1; i <= n; i++)
                    if (dist[i] > dist[s]) s = i;
            for (int i = 1; i <= n; i++) dist[i] = inf;
            dist[s] = 0;
            q.push(s);
            while (!q.empty())
            {
                    int u = q.front();
                    q.pop();
                    for (int i = head[u]; i; i = e[i].nxt)
                    {
                            int v = e[i].to , w = e[i].w;
                            if (dist[u] + w < dist[v])
                            {
                                    dist[v] = dist[u] + w;
                                    q.push(v);
                            }
                    }
            }
            ll ret = 0;
            for (int i = 1; i <= n; i++) chkmax(ret , dist[i]);
            return ret;
    }
    inline bool check(ll mid)
    {
            cnt = 0;
            dfs(1 , 0 , mid);
            return cnt >= m;        
    }
    
    int main()
    {
            
            read(n); read(m);
            ll l = 0 , r = 0 , ans = 0;
            for (int i = 1; i < n; i++)
            {
                    int u , v , w;
                    read(u); read(v); read(w);
                    addedge(u , v , w);
                    addedge(v , u , w);
            }
            r = diameter();
            while (l <= r)
            {
                    ll mid = (l + r) >> 1;
                    if (check(mid))
                    {
                            ans = mid;
                            l = mid + 1;        
                    }    else r = mid - 1;
            }
            printf("%lld
    " , ans);
            
            return 0;
        
    }
  • 相关阅读:
    算法笔记_148:有向图欧拉回路求解(Java)
    算法笔记_147:有向图欧拉回路判断应用(Java)
    算法笔记_146:TarJan算法的应用(Java)
    算法笔记_145:拓扑排序的应用(Java)
    第十六章、例行性工作排程 (crontab)
    第十五章、磁碟配额(Quota)与进阶文件系统管理
    第十四章、Linux 账号管理与 ACL 权限配置
    第十三章、学习 Shell Scripts
    第十二章、正规表示法与文件格式化处理
    第十一章、认识与学习 BASH
  • 原文地址:https://www.cnblogs.com/evenbao/p/10035516.html
Copyright © 2020-2023  润新知