其他分享
首页 > 其他分享> > 图论训练之十二

图论训练之十二

作者:互联网

https://www.luogu.org/problem/P5021

好早以前做过,但现在再看发现当时根本没理解

分析:

题目大意:

找出互不相交的m条路径,使得这m条路径中最短的尽量最长(原图是一颗树)!!!

最长的最短:二分答案?长度大于等于mid的有没有m个

树?树?树?树形dp?

当我们二分这个答案后,关键就在于如何判断能否组成m条路径

这真的很棘手,我以前从来没遇到过找路径的

关键就在于这是颗树

树!树!树!

考虑当前节点u与其子树v1,v2,v3...所连接的边(u,v1),(u,v2),(u,v3)....

发现经过这些边的路径至多只能是一条!!!!

但还要先满足<=mid的限制条件

剩余的边怎么办?就两两组合

两两要最优组合
怎么最优组合?
尽量两两组合后越靠近mid就越好
非multiset的一个二分解决
multiset的一个lower_bound解决
将失配的上传

非multiset code:

#include<bits/stdc++.h>
using namespace std;
const int MAXN = 5e4 + 5, MAXM = MAXN << 1;
const int INF = 0x7fffffff;
int head[MAXM], nxt[MAXM], v[MAXM], w[MAXM], cnt;
int n, m;
int root;
inline void Addline(int x, int y, int z)
{
    v[cnt] = y, w[cnt] = z;
    nxt[cnt] = head[x], head[x] = cnt++;
    return;
}
int dp[MAXN],tag[MAXN];//dp是向上贡献的最长链长度
int que[MAXN], tail;
int res;
inline void DFS(int x, int from, int lim)
{
    for (int i = head[x]; ~i; i = nxt[i])
        if (v[i] != from)
            DFS(v[i], x, lim);
    tail = 0;
    for (int i = head[x]; ~i; i = nxt[i])if (v[i] != from)que[++tail] = dp[v[i]] + w[i];
    sort(que + 1, que + tail + 1);
    for (int i = tail; i >= 1 && que[i] >= lim; i--)tail--, res--;
    for (int i = 1; i <= tail; i++)
        if (tag[i] != x)
        {
            int l = i + 1, r = tail, pst = tail + 1;
            while (l <= r)
            {int mid=l+r>>1;
                if (que[i] + que[mid] >= lim)
                    pst = mid, r = mid - 1;
                else
                    l = mid + 1;
            }
            while (tag[pst] == x && pst <= tail)pst++;
            if (pst <= tail)
                tag[i] = tag[pst] = x, res--;
        }
    dp[x] = 0;
    for (int i = tail; i >= 1; i--)
        if (tag[i] != x){dp[x] = que[i];break;}
    return;
}
signed main(void)
{
    memset(head, -1, sizeof head);
    cin >> n >> m;
    for (int i = 1, x, y, z; i < n; i++)
    {
        scanf("%d %d %d", &x, &y, &z);
        Addline(x, y, z), Addline(y, x, z);
    }
    root = rand() % n + 1;
    int l = 0, r = INF, ans = 0;
    while (l <= r)
    {int mid=l+r>>1;
        res = m;
        memset(tag, false, sizeof tag);
        DFS(root, 0, mid);
        if (res <= 0)
            ans = mid, l = mid + 1;
        else
            r = mid - 1;
    }
    cout << ans << endl;
    return 0;
}

mutiset code:

#include<bits/stdc++.h>
using namespace std;
int cnt,fst[50005],nxt[100005],to[100005],w[100005];
int n,m,l=2147400000,r,mid,ans,f[50005],g[50005];
multiset <int> s;
multiset <int>::iterator it;
void AddEdge(int u,int v,int c)//链式前向星
{
    to[++cnt]=v;
    nxt[cnt]=fst[u];
    fst[u]=cnt;
    w[cnt]=c;
}
void Dfs(int u,int faz)//树形DP部分
{
    for(int i=fst[u];i;i=nxt[i])
    {
        int v=to[i];
        if(v==faz) continue;
        Dfs(v,u);
        f[u]+=f[v];
    }
    for(int i=fst[u];i;i=nxt[i])
    {
        int v=to[i];
        if(v==faz) continue;
        s.insert(g[v]+w[i]);
    }
    while(!s.empty())
    {
        int now=*s.rbegin();
        if(now>=mid)
        {
            it=s.find(now);
            f[u]++;
            s.erase(it);
        }
        else break;
    }
    while(!s.empty())
    {
        int now=*s.begin();
        s.erase(s.begin());
        it=s.lower_bound(mid-now);//找不到就返回end()
        if(it==s.end()) g[u]=now;
        else
        {
            f[u]++;
            s.erase(it);
        }
    }
}
int main()
{
    scanf("%d %d",&n,&m);
    for(int i=1;i<n;i++)
    {
        int x,y,z;
        scanf("%d %d %d",&x,&y,&z);
        AddEdge(x,y,z);
        AddEdge(y,x,z);
        l=min(l,z);
        r+=z;
    }
    while(l<=r)//二分答案
    {
        mid=l+r>>1;
        memset(f,0,sizeof(f));
        memset(g,0,sizeof(g));
        Dfs(1,0);
        if(f[1]>=m)
        {
            ans=max(ans,mid);
            l=mid+1;
        }
        else r=mid-1;
    }
    printf("%d",ans);
    return 0;
}

标签:图论,训练,int,十二,mid,fst,cnt,multiset,now
来源: https://www.cnblogs.com/wzxbeliever/p/11740782.html