[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 << "\n";
    
    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\n" , 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\n" , ans);
        
        return 0;
    
}

 

posted @ 2018-11-28 22:38  evenbao  阅读(352)  评论(0)    收藏  举报