P1967 [NOIP 2013 提高组] 货车运输(树链剖分+线段树)

文章目录

  • 题目要求
  • 一、解题思路
  • 二、解题过程
    • 1.数据结构
    • 2.求最小生成树(Kruskal算法)
    • 2.答案计算(TCD+SegementTree)
  • AC代码


题目要求

A 国有 n 座城市,编号从 1 到 n,城市之间有 m 条双向道路。每一条道路对车辆都有重量限制,简称限重。
现在有 q 辆货车在运输货物, 司机们想知道每辆车在不超过车辆限重的情况下,最多能运多重的货物。


一、解题思路

本题求一条路径,使得其在不超过限制重量的前提下,载货重量的最大价值(最小值最大化),我们可以考虑求最大生成树,运用贪心的思想,求解答案。
根据输入数据,是一颗无根树,我们想到树链剖分,结合线段树维护区间最小值

二、解题过程

1.数据结构

代码如下(示例):

struct DSU
{
    int f[N];
    void init(int n) { for (int i = 1; i <= n; i++) f[i] = i;}
    int find(int u) { return u == f[u] ? u : f[u] = find(f[u]); }
    int merge(int u,int v)
    {
        int t1,t2;   t1 = find(u);  t2 = find(v);
        if (t1 != t2)
        {
            f[t2] = t1;   return 1;
        }
        return 0;
    }
}dsu;
struct seg
{
    int l,r,mn;
}tree[N*4];
struct edg
{
    int u,v,w;
};
struct edge
{
    int v,w,ne;
}e[100005];

这里封装了并查集,用于求最小生成树。

2.求最小生成树(Kruskal算法)

void Kruskal()
{
    dsu.init(n);
    sort(el.begin(),el.end(),[] (const edg &pa,const edg &pb) { return pa.w > pb.w; });
    int ct = 0;
    for (auto [u,v,w] : el)
    {
        if (dsu.merge(u,v))
        {
            add(u,v,w);  add(v,u,w);
            ct++;
        }
        if (ct == n-1)
        {
            break;
        }
    }
}

先使用Kruskal求最大生成树(贪心思想),并建立新图,新图使用链式前向星。


2.答案计算(TCD+SegementTree)

void dfs1(int u,int f);
void dfs2(int u,int t);
int ls(int p);
int rs(int p);
void pushup(int p);
void buildTree(int p,int l,int r);
int query(int p,int l,int r);

结合树剖+线段树求区间最小值即可得到答案。

AC代码

#include 
using namespace std;
#define IOS ios::sync_with_stdio(false),cin.tie(0),cout.tie(0)
const int N = 10005;
struct DSU
{
    int f[N];
    void init(int n) { for (int i = 1; i <= n; i++) f[i] = i;}
    int find(int u) { return u == f[u] ? u : f[u] = find(f[u]); }
    int merge(int u,int v)
    {
        int t1,t2;   t1 = find(u);  t2 = find(v);
        if (t1 != t2)
        {
            f[t2] = t1;   return 1;
        }
        return 0;
    }
}dsu;
struct seg
{
    int l,r,mn;
}tree[N*4];
struct edg
{
    int u,v,w;
};
struct edge
{
    int v,w,ne;
}e[100005];
int depth[N],sz[N],son[N],fa[N],top[N],id[N],nw[N],ww[N],cnt;
int h[N],idx,n,m,q;
vector<edg> el;
void add(int u,int v,int w);
void Kruskal();
int query_mn(int u,int v);
void dfs1(int u,int f);
void dfs2(int u,int t);
int ls(int p);
int rs(int p);
void pushup(int p);
void buildTree(int p,int l,int r);
int query(int p,int l,int r);
int main()
{
    IOS;   cin >> n >> m;
    memset(h,-1,sizeof(h));
    for (int i = 1;i <= m; i++)
    {
        int u,v,w;  cin >> u >> v >> w;
        el.push_back({u,v,w});
    }
    Kruskal();
    for (int i = 1; i <= n; i++)
    {
        if (sz[i])  continue;
        dfs1(i,0);
    }
    for (int i = 1; i <= n; i++)
    {
        if (id[i])  continue;
        dfs2(i,i);
    }
    buildTree(1,1,cnt);
    cin >> q;
    while (q--)
    {
        int l,r;  cin >> l >> r;
        if (l > r)  swap(l,r);
        cout << query_mn(l,r) << "\n";
    }
    return 0;
}
void add(int u,int v,int w)
{
    e[idx] = {v,w,h[u]};
    h[u] = idx++;
}
void Kruskal()
{
    dsu.init(n);
    sort(el.begin(),el.end(),[] (const edg &pa,const edg &pb) { return pa.w > pb.w; });
    int ct = 0;
    for (auto [u,v,w] : el)
    {
        if (dsu.merge(u,v))
        {
            add(u,v,w);  add(v,u,w);
            ct++;
        }
        if (ct == n-1)
        {
            break;
        }
    }
}
int query_mn(int u,int v)
{
    if (dsu.find(u) != dsu.find(v))   return -1;
    int mn = INT32_MAX;
    while (top[u] != top[v])
    {
        if (depth[top[u]] < depth[top[v]])  swap(u,v);
        mn = min(mn,query(1,id[top[u]],id[u]));
        u = fa[top[u]];
    }
    if (u == v)   return mn;
    if (depth[u] < depth[v])  swap(u,v);
    mn = min(mn,query(1,id[v]+1,id[u]));
    return mn;
}
void dfs1(int u,int f)
{
    depth[u] = depth[f] + 1;
    sz[u] = 1;   fa[u] = f;
    for (int i = h[u]; ~i; i = e[i].ne)
    {
        int v = e[i].v;
        if (v == f)   continue;
        ww[v] = e[i].w;
        dfs1(v,u);
        sz[u] += sz[v];
        if (sz[son[u]] < sz[v])  son[u] = v;
    }
}
void dfs2(int u,int t)
{
    top[u] = t;   id[u] = ++cnt;   nw[cnt] = ww[u];
    if (!son[u])  return;
    dfs2(son[u],t);
    for (int i = h[u]; ~i; i = e[i].ne)
    {
        int v = e[i].v;
        if (v == fa[u] || v == son[u])  continue;
        dfs2(v,v);
    }
}
int ls(int p) { return p << 1; }
int rs(int p) { return p << 1 | 1; }
void pushup(int p) { tree[p].mn = min(tree[ls(p)].mn,tree[rs(p)].mn); }
void buildTree(int p,int l,int r)
{
    tree[p] = {l,r,nw[l]};
    if (l == r)  return;
    int mid = l + r >> 1;
    buildTree(ls(p),l,mid);
    buildTree(rs(p),mid+1,r);
    pushup(p);
}
int query(int p,int l,int r)
{
    if (l <= tree[p].l && tree[p].r <= r)  return tree[p].mn;
    int mid = tree[p].l + tree[p].r >> 1,ans = INT32_MAX;
    if (l <= mid)  ans = min(ans,query(ls(p),l,r));
    if (r > mid)  ans = min(ans,query(rs(p),l,r));
    return ans;
}

你可能感兴趣的:(cocoa,c++,算法,贪心算法,数据结构)