美团2021届秋季校园招聘笔试-小美的区域会议——人工加入限制+树形dp

总结:集合划分的思想+树形dp+定义序关系(用于去重)

直接按树形dp想,没有思路,因为你强行记录所选取方案集合的权值最小和最大显然不可做。

此时考虑加入一些假设(限制)。假设当前枚举的点i是所选子树的点权最小的点(假设是最大的点也行)。不难发现遍历所有i对应的合法方案集合,是对所有合法方案的一个划分。以i为树根跑树形dp,保证所有选择的点的权值都在a[rt]~a[rt]+k即可。因为子树答案只会被使用一次,所以dp数组可以省略。时间复杂度:O(n^2)

但是我们还面临一个问题:如果一个合法方案有多个最小点权的点,则以上方法会重复统计。因此定义一个序关系(注:在单调栈相关的一些题目里也见过这种技巧,我把它叫做”非对称定义“。):当a[v] == a[rt],只有编号小于rt的那些点v对应的子树才能被选择。在此定义下,某个合法方案只会被编号最大的那个点权最小的点统计。

代码
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
#define rep(i,a,b) for(int i = (a);i <= (b);++i)
#define re_(i,a,b) for(int i = (a);i < (b);++i)
#define dwn(i,a,b) for(int i = (a);i >= (b);--i)

const int N = 2e3 + 5;
const int mod = 1e9 + 7;

int n,k,a[N];vector<int> G[N];

void dbg(){puts("");}
template<typename T, typename... R>void dbg(const T &f, const R &... r) {
    cout << f << " ";
    dbg(r...);
}
template<typename Type>inline void read(Type &xx){
    Type f = 1;char ch;xx = 0;
    for(ch = getchar();ch < '0' || ch > '9';ch = getchar()) if(ch == '-') f = -1;
    for(;ch >= '0' && ch <= '9';ch = getchar()) xx = xx * 10 + ch - '0';
    xx *= f;
}

LL dfs(int u,int ufa,int rt){
    LL ans = 1;
    for(int v: G[u]){
        if(v == ufa) continue;
        if((a[v] == a[rt] && v < rt) || (a[rt] < a[v] && a[v] <= a[rt]+k)){
            (ans *= (1+dfs(v,u,rt))%mod) %= mod;
        }
    }
    return ans;
}

int main(int argc, char** argv) {
    read(n);read(k);
    re_(_,1,n){
        int p1,p2;read(p1);read(p2);
        G[p1].push_back(p2);
        G[p2].push_back(p1);
    }
    rep(i,1,n) read(a[i]);
    int ans = 0;
    rep(i,1,n) (ans += dfs(i,0,i)) %= mod;
    printf("%d\n",ans);
    return 0;
}
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值