zoukankan      html  css  js  c++  java
  • 图论训练之十三

    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;
    }
    
  • 相关阅读:
    Jquery 图片走马灯效果原理
    jquery实现跑马灯效果(一)
    jQuery实现网页放大镜功能
    JavaScript仿淘宝实现放大镜效果的实例
    jquery实现放大镜简单方法
    jQuery实现放大镜效果
    小白jquery横向菜单弹出菜单制作
    转载 Log4j2在WEB项目中配置
    Struts2关于命名空间的例子
    转载关于struts命名空间的一则报警
  • 原文地址:https://www.cnblogs.com/wzxbeliever/p/11740782.html
Copyright © 2011-2022 走看看