首页 > 其他分享 >CF1746D(记忆化搜索,DP,贪心)

CF1746D(记忆化搜索,DP,贪心)

时间:2022-10-16 20:00:07浏览次数:68  
标签:CF1746D int sum define ans include adj DP 贪心

CF1746D(记忆化搜索,DP,贪心)

https://codeforces.com/contest/1746/problem/d

题意

给一棵树,树上每个点有一个权值 \(s_i\), 有一个整数 \(k\)。表示从根节点出发的简单路径的数量。给出约束:对一点 \(u\) ,它的儿子所经过的简单路径的数量差不能超过1。

设每个点经过的简单路径的数量为 \(c_i\)。最大化 \(\sum c_i s_i\)。

思路

两个显然的结论,一定要让路径走到底,到达叶子才有可能最优。从某点经过向下的简单路径对它的儿子的分配数量一定是 \(\lfloor \frac{c_i}{son} \rfloor\) 或者是 \(\lfloor \frac{c_i}{son} \rfloor+ 1\)。

然后考虑到可以用 \(f(u, sum)\) 表示所有子问题的解,考虑记忆化搜索。

定义 \(f[u][sum]\) 表示当前点 \(u\) 分配路径数量为 \(sum\) 的答案。

转移就是所有儿子的 \(f[v][\frac{sum}{son}]\) 或者 \(f[v][\frac{sum}{son}+1]\) 的和。问题是如何选择这两种情况。最显然的一个解法是做一个01背包,显然时间上过不去。这里和普通背包不同的是限制了取 "1" 的数量,这时可以利用这一个条件进行贪心排序:先全部选 "0",再贪心选择 "1" 进行替换。什么样的 "1" 最优呢:按照“0”和“1”的差值排序取最大即可。

于是可以完成\(O(sonlog(son))\) 的转移。

最后如果树是一条链,每次需要向两个情况搜索,且sum无法很快的变成1,算法复杂度将变成指数级的。所以需要特判掉链的情况。对于其他情况,由于 \(sum\) 很快会减小,树的深度最多向下 \(logn\) 层,所以复杂度是正确的。

#include<iostream>
#include<cstring>
#include<algorithm>
#include<vector>
#include<set>
#include<bitset>
#include<queue>
#include<map>
#include<stack>
#include<string>
#include<random>
#include<cassert>
#include<functional>
#include<iomanip>
#define inf 0x3f3f3f3f
#define linf 0x3f3f3f3f3f3f3f3fll
#define endl '\n'
#define ll long long
#define int long long
#define SZ(x) (int)x.size()
#define rep(i,a,n) for(int i = (a);i <= (n);i++)
#define dec(i,n,a) for(int i = (n);i >= (a);i--)
using namespace std;
using PII = pair<int, int>;
mt19937 mrand(random_device{}());
int rnd(int x) { return mrand() % x;}
const int N =10 + 2e5 ,mod=1e9 + 7;

void solve()
{    
    int n, k; cin >> n >> k;
    vector<vector<int>> adj(n + 1);
    rep(i,1, n - 1) {
        int p; cin >> p;
        adj[p].emplace_back(i + 1);
    }
    vector<int> s(n + 1); rep(i, 1, n) cin >> s[i];

    map<int, map<int,int>> f;// f[][] = ?
    function<int(int, int)> dfs = [&](int u, int sum) {
        if(f[u].count(sum)) return f[u][sum];
        int ans = sum * s[u];
        
        if(!SZ(adj[u])) {
            return f[u][sum] = ans;
        }

        vector<PII> t;
        int o = sum % SZ(adj[u]);
        if(o) {
            for(auto v : adj[u]) {
                PII cur;
                cur.first = dfs(v, sum / SZ(adj[u]));
                cur.second = dfs(v, sum / SZ(adj[u]) + 1);
                t.push_back(cur);

                ans += cur.first;
            }

            sort(t.begin(), t.end(), [&](PII &A, PII &B) {
                return A.second - A.first > B.second - B.first;
            });

            rep(i, 0, o - 1) ans -= t[i].first, ans += t[i].second;
        }else {
            for(auto v : adj[u]) {
                ans += dfs(v, sum / SZ(adj[u]));
            }
        }
        return f[u][sum] = ans;
    };

    cout << dfs(1, k) << endl;
}
signed main()
{
    ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);

    int T;cin>>T;
    while(T--)
        solve();

    return 0;
}

标签:CF1746D,int,sum,define,ans,include,adj,DP,贪心
From: https://www.cnblogs.com/Mxrush/p/16796943.html

相关文章

  • Leetcode 6207 -- dp/思维/双指针
    题目描述maxmin思路思维代码classSolution{funccountSubarrays(_nums:[Int],_minK:Int,_maxK:Int)->Int{letsegments=nums.split......
  • AcWing 4706 -- 树形DP/DFS
    题目描述4706.最短路程思路dfs代码#include<iostream>#include<cstring>#include<algorithm>#include<vector>usingnamespacestd;constintN=100......
  • 2020CCPC秦皇岛-K. Kingdom's Power(树形DP + 贪心)
    题意给出一个有n个节点的有根树,1为根节点,根节点有无穷多个兵,每一秒可以让任意一个兵向任意一个地方移动一步,兵所到的点被占领,问最少需要经过多少秒,才能将所有的点都占领......
  • Sub-process /usr/bin/dpkg returned an error code (1)问题
     在用apt-get安装软件包的时候遇到E:Sub-process/usr/bin/dpkgreturnedanerrorcode(1)问题,解决方法如下:cd/var/lib/dpkg/sudomvinfo/info_bak......
  • 驱动开发:内核枚举DpcTimer定时器
    在笔者上一篇文章《驱动开发:内核枚举IoTimer定时器》中我们通过IoInitializeTimer这个API函数为跳板,向下扫描特征码获取到了IopTimerQueueHead也就是IO定时器的队列头,本章......
  • 【区间DP】ABC273F. Hammer 2
    ABC273F.Hammer2Difficulty:2277、关路灯模型区间DP题意略。思路设计dp状态:\(f[l][r][0/1]\)表示走完区间[l,r]最后待在l(0)或r(1)处的最小移动距离总和......
  • 【经典题】栈和贪心法
    题目:769.最多能完成排序的块给定一个长度为n的整数数组arr,它表示在[0,n-1]范围内的整数的排列。我们将arr分割成若干块(即分区),并对每个块单独排序。将它......
  • DP 优化
    决策单调性四边形不等式对于一个序列\(w\),称其满足四边形不等式当且仅当:\[\foralla<b\lec<d,w_{a,d}+w_{b,c}\gew_{a,c}+w_{b,d}\]\(\foralli,j,w_{i,j+1}+w_{i......
  • 用户数据报协议UDP
    UDP的首部格式如下:(1)源端口,源端口号。在需要对方回信时选用。不需要时可用全0。⑵目的端口,目的端口号。这在终点交付报文时必须使用。⑶长度,UDP用户数据报的长度,其最......
  • DPDK基础概念和原理
    1、DPDK做什么的?数据平面开发套件(DPDK,DataPlaneDevelopmentKit)dpdk为Intel处理器架构下用户空间高效的数据包处理提供了库函数和驱动的支持,它不同于Linux系......