zoukankan      html  css  js  c++  java
  • CodeForces

    题目链接

    题目大意

      你可以在一棵数的边上填上(0~n-2)中的一个数,每个数只用一次,问树上任意两个不同点的mex(u, v)之和是多少?mex(u,v)代表两点之间的简单路径上没有出现的最小的非负整数。

    解题思路

      对于任意一条路径,如果不包含权值为0的边,那么结果必定为0,所以我们从一条权值为0的边开始考虑,设左边的点为u,右边的点为v,(sz[u])为以u为根不包括链上其他点的子树的结点数量,(sz[v])也是如此,那么这条权值为0的边的贡献就是(sz[u]*sz[v]),然后权值为1的边,肯定只有在与权值为0的边相连的情况下贡献才最大(如果不与之相连,对于其与0号边在同一路径上的路径,将他移动到0号边的一边并不影响其贡献,并且他们中间的边的贡献也会增加)。而对于权值为2的边,也是一样(可以将0和1两条边视为权值为0的边,权值为2的边视为权值为1的边)。所以可以看出,我们可以通过一条链来算出整个树的值。
      我们预处理出以任意点为根,其子树的大小,然后跑树形dp,对于当前的长度为len的链,既有可能是一条长度为len-1的子链从左边扩展来的,也有可能是从右边扩展来的,两者取max即可,而加入每条边的贡献都是两点的子树大小的乘积(不包括链上的其他点)。

    代码

    const int maxn = 3e3+10;
    const int maxm = 2e6+10;
    vector<int> e[maxn];
    int n, f[maxn][maxn], sz[maxn][maxn];
    ll dp[maxn][maxn];
    void gsz(int u, int p, int rt) {
        sz[rt][u] = 1;
        f[rt][u] = p;
        for (auto v : e[u]) 
            if (v!=p) gsz(v, u, rt), sz[rt][u] += sz[rt][v];
    }
    ll sl(int l, int r) {
        if (l==r) return 0;
        if (dp[l][r]!=-1) return dp[l][r];
        return dp[l][r] = 1LL*sz[r][l]*sz[l][r]+max(sl(r, f[r][l]), sl(l, f[l][r]));
    }
    int main() {
        clr(dp, 0xff);
        cin >> n;
        for (int i = 1, u, v; i<n; ++i) {
            cin >> u >> v;
            e[u].push_back(v);
            e[v].push_back(u);
        }
        for (int i = 1; i<=n; ++i) gsz(i, 0, i);
        ll ans = 0;
        for (int i = 1; i<=n; ++i)
            for (int j = 1; j<=n; ++j)
                ans = max(ans, sl(i, j));
        cout << ans << endl;
        return 0;
    }
    
  • 相关阅读:
    1041 考试座位号
    1040 有几个PAT
    1039 到底买不买
    1038 统计同成绩学生
    1037 在霍格沃茨找零钱
    1036 跟奥巴马一起编程
    1035 插入与归并
    vue-router--路由传参
    vue-router--路由原理
    vuex--在computed中使用
  • 原文地址:https://www.cnblogs.com/shuitiangong/p/14552085.html
Copyright © 2011-2022 走看看