zoukankan      html  css  js  c++  java
  • 【XSY1905】【XSY2761】新访问计划 二分 树型DP

    题目描述

      给你一棵树,你要从(1)号点出发,经过这棵树的每条边至少一次,最后回到(1)号点,经过一条边要花费(w_i)的时间。

      你还可以乘车,从一个点取另一个点,需要花费(c)的时间。

      你最多做(k)次车。

      问最短时间。

      (kleq nleq 20000,w,cleq 50000)

    题解

      我们考虑把最终路线中坐车的部分替换成走路。

      那么显然不会经过一条边超过两次。

      但是每条边都要经过者少一次,所以每条边只能被一个坐车的路线覆盖。

      所以我们要选择不超过(k)条不相交的链,把这些链用(c)的代价覆盖掉。

      可以用树上背包做。

      时间复杂度:(O(nk))

      还有有一个网络流的做法:每次找树上最长链,然后用(c)的代价覆盖掉,即把路径上的边权取反。

      正解:

      如果我们可以调整乘车的代价,并把乘车次数设为无限次,那么当最优方案的乘车次数不超过(k)时最优方案的路线就是最优路线。

      这个东西可以用一次树型DP解决。

      设(f_i)为从(i)开始遍历以(i)为根的子树并回到(i)的最小代价和乘车次数,(g_i)为从(i)开始遍历以(i)为根的子树并乘车回到(i)的最小代价和乘车次数。

      然后随便DP一下就行了。

      可以观察到,乘车次数是随着乘车代价单调下降的(可能是非连续的),所以可以二分乘车代价,得到答案。

      时间复杂度:(O(nlog nw))

    代码

    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #include<utility>
    using namespace std;
    typedef unsigned un;
    typedef pair<un,un> pii;
    const int inf=0x3fffffff;
    vector<pii> t[100010];
    int n,k,c;
    un cost;
    pii f[100010];
    pii g[100010];
    pii operator +(pii a,pii b)
    {
    	return pii(a.first+b.first,a.second+b.second);
    }
    pii operator -(pii a,pii b)
    {
    	return pii(a.first-b.first,a.second-b.second);
    }
    void dp(int u,int fa)
    {
    	f[u]=pii(0,0);
    	g[u]=pii(cost,1);
    	for(auto a:t[u])
    		if(a.first!=fa)
    		{
    			int v=a.first;
    			int w=a.second;
    			dp(v,u);
    			pii f1=pii(inf,0);
    			pii g1=pii(inf,0);
    
    			f1=min(f1,f[u]+f[v]+pii(w,0));
    			f1=min(f1,f[u]+f[v]+pii(cost,1));
    			f1=min(f1,f[u]+g[v]);
    			f1=min(f1,g[u]+f[v]);
    			f1=min(f1,g[u]+g[v]-pii(cost,1));
    
    			g1=min(g1,f[u]+f[v]+pii(cost,1));
    			g1=min(g1,f[u]+g[v]);
    			g1=min(g1,g[u]+f[v]+pii(w,0));
    			g1=min(g1,g[u]+g[v]);
    
    			f[u]=f1;
    			g[u]=g1;
    		}
    }
    void solve()
    {
    	for(int i=1;i<=n;i++)
    		t[i].clear();
    	int x,y,z;
    	int sum=0;
    	for(int i=1;i<n;i++)
    	{
    		scanf("%d%d%d",&x,&y,&z);
    		x++;
    		y++;
    		t[x].push_back(pii(y,z));
    		t[y].push_back(pii(x,z));
    		sum+=z;
    	}
    	cost=c;
    	dp(1,0);
    	if(f[1].second<=k)
    	{
    		printf("%d
    ",sum+f[1].first);
    		return;
    	}
    	int l=0,r=inf;
    	while(l<r)
    	{
    		cost=(l+r)>>1;
    		dp(1,0);
    		if(f[1].second>k)
    			l=cost+1;
    		else
    			r=cost;
    	}
    	cost=l;
    	dp(1,0);
    	int ans=f[1].first-k*(l-c)+sum;
    	printf("%d
    ",ans);
    }
    int main()
    {
    #ifndef ONLINE_JUDGE
    	freopen("b.in","r",stdin);
    	freopen("b.out","w",stdout);
    #endif
    	while(~scanf("%d%d%d",&n,&k,&c))
    		solve();
    	return 0;
    }
    
  • 相关阅读:
    Median Value
    237. Delete Node in a Linked List
    206. Reverse Linked List
    160. Intersection of Two Linked Lists
    83. Remove Duplicates from Sorted List
    21. Merge Two Sorted Lists
    477. Total Hamming Distance
    421. Maximum XOR of Two Numbers in an Array
    397. Integer Replacement
    318. Maximum Product of Word Lengths
  • 原文地址:https://www.cnblogs.com/ywwyww/p/8622573.html
Copyright © 2011-2022 走看看