zoukankan      html  css  js  c++  java
  • [HNOI 2018] 排列

    [题目链接]

              https://www.lydsy.com/JudgeOnline/problem.php?id=5289

    [算法]

             题目中的限制条件可看作是 : 第i个数必须排在所有权值为i的数前面

             那么 , 我们枚举每一个数 , 向所有权值为当前枚举的数的下标的数连边 , 不难发现 , 若无解 , 则说明图中存在环 , 我们可以通过拓扑排序判断图中是否存在环

             否则 , 仔细观察后发现这其实是由若干棵树组成的森林 , 我们建立(n + 1)号节点 , 将该节点的权值设为0,并向森林中每一棵树的根节点连边 

             问题就转化为了 : 有一棵树 , 你需要给这棵树染色 , 除根节点外 , 染了父亲节点才能染子节点 , 每次染色花费的代价为 :  i * Wi , 其中i代表的是第几次染色 , 要求最大化染色的代价

             我们可以发现一个性质 : 权值最小的节点一定在其父节点被染色后立即染色 

             根据这个性质 , 我们不妨找出权值最小的节点 , 将其与父节点合并 , 合并后的权值为它们权值和的平均值

             重复以上过程N次 , 就得到了答案

             具体实现时 , 我们需要一个堆维护权值最小的节点 , 此外 , 还需用并查集快速找出某个点合并后的父亲

             时间复杂度 : O(NlogN)

    [代码]

              

    #include<bits/stdc++.h>
    using namespace std;
    #define MAXN 500010
    typedef long long ll;
    
    int n,size;
    int fa[MAXN],deg[MAXN],hp[MAXN],pos[MAXN],s[MAXN];
    ll ans;
    vector< int > e[MAXN],ver[MAXN];
    pair<ll,int> a[MAXN];
    
    template <typename T> inline void read(T &x)
    {
        ll 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 bool cmp(int x,int y) 
    {
        return a[x].first * a[y].second < a[x].second * a[y].first;
    }
    inline int get_root(int x)
    {
            if (s[x] == x) return x;
            return s[x] = get_root(s[x]);
    }
    inline bool topsort()
    {
            int l,r;
            static int q[MAXN];
            l = 1; r = 0;
            for (register int i = 1; i <= n; i++)
            {
                    if (!deg[i])
                            q[++r] = i;
            }
            while (l <= r)
            {
                    int u = q[l];
                    l++;
                    for (register int i = 0; i < (int)e[u].size(); i++)
                    {
                            int v = e[u][i];
                            if ((--deg[v]) == 0)
                                    q[++r] = v;
                    }
            }
            for (register int i = 1; i <= n; i++)
            {
                    if (deg[i])
                            return false;
            }
            return true;
    }
    inline void up(int x)
    {
            int fa = x >> 1;
            if (x == 1) return;
            if (cmp(hp[x],hp[fa]))
            {
                        swap(pos[hp[fa]],pos[hp[x]]);
                  swap(hp[x],hp[fa]);
                  up(fa);
            }
    }
    inline void down(int x)
    {
            int son = x << 1;
            if (son > size) return;
            if (son + 1 <= size && cmp(hp[son + 1],hp[son])) son++;
            if (cmp(hp[son],hp[x]))
            {
                    swap(pos[hp[son]],pos[hp[x]]);        
                swap(hp[son],hp[x]);
                down(son); 
            }
    }
    inline void push(int a)
    {
            hp[++size] = a;
            pos[a] = size;
            up(size);
    }
    inline void pop()
    {
                swap(pos[hp[1]],pos[hp[size]]);
          swap(hp[1],hp[size--]);
          down(1);
    }
    inline int top()
    {
          return hp[1];
    }
    inline bool is_empty()
    {
          return size == 0;
    }
    
    int main() 
    {
            
          read(n);
          for (register int i = 1; i <= n; i++)
          {
              int x;
              read(x);
              ver[x].push_back(i);
          }
          for (register int i = 1; i <= n; i++) 
          {
                ll x;
                read(x);
                a[i] = make_pair(x,1); 
          }
          for (register int i = 1; i <= n; i++)
          {
                  for (register int j = 0; j < (int)ver[i].size(); j++)
                  {
                          e[i].push_back(ver[i][j]);
                          fa[ver[i][j]] = i;    
                          deg[ver[i][j]]++;
                  }        
          }
          for (register int i = 1; i <= n; i++) 
          {
              if (!fa[i])
              {
                  e[n + 1].push_back(i);
                  deg[i]++;
                  fa[i] = n + 1;
              }
          }
          a[++n] = make_pair(0,1);
          for (register int i = 1; i <= n; i++) s[i] = i;
          if (!topsort())
          {
              printf("-1
    ");
                return 0;
          }
          for (register int i = 1; i < n; i++) push(i);
          for (register int i = 1; i < n; i++)
          {
              int v = top() , f = get_root(fa[v]);
              pop();
              ans += a[v].first * a[f].second;
              a[f].first += a[v].first;
              a[f].second += a[v].second;
              s[v] = f;
              if (f != n) up(pos[f]); 
          }
          printf("%lld
    ",ans);
          
          return 0;
      
    }

             

             

  • 相关阅读:
    doker基础用法
    docker容器技术基础入门
    流模式
    装饰器模式
    适配器模式
    组合模式
    桥接模式
    观察者模式
    注册模式
    模板模式
  • 原文地址:https://www.cnblogs.com/evenbao/p/9549454.html
Copyright © 2011-2022 走看看