zoukankan      html  css  js  c++  java
  • Codeforces Round #418 (Div. 2) D. An overnight dance in discotheque

    Codeforces Round #418 (Div. 2) D. An overnight dance in discotheque


    题意: 给(n(n <= 1000))个圆,圆与圆之间不存在相交关系,只存在包含与不包含关系,现在问你把这一堆圆分成两组,求每组圆的异或并和的最大值。

    思路:最简单的做法是贪心的做法,算出每个圆被包含的次数,为偶数则贡献为正,奇数为贡献为负。
    现在主要学习一下树形dp的做法
    由于圆不存在相交的情况,所以可以把所有的圆建成树,即把每棵树上的点分成两组,分别建成两棵树
    使得ans=所有偶数深度的点(sum{area_i}) - 所有奇数深度的点(sum{area_j})最大。

    由于每棵树是独立的,对每棵树做树形dp就好了
    然而题解的状态定义简直难想的一p,没做过类似套路的题,根本想不出嘛,还是贪心做这道题比较靠谱
    不过套路还是要学的
    dp[u][p1][p2] 表示假定在u的祖先中p1个点给了第一颗树,剩下p2个点给了第二棵树,以u为根的子树能获得的最大价值(题做的少,感觉这样的定义好奇特啊)
    考虑u分给第一棵树 或者 第二颗树 则有如下转移方程
    (egin{equation} dp[u][p1][p2] = max egin{cases} sum_{v epsilon child(u)} dp[v][p1+1][p2]+(p1\%2==0?1:-1)*area[u] \ sum_{vepsilon child(u)}dp[v][p1][p2+1]+(p2\%2==0?1:-1)*area[u] end{cases} end{equation})

    这个方程看起来似乎可以从上往下做状态转移啊,仔细一想树一旦分叉,祖先结点就被考虑多次,转移就出错了,所以只能从下到上。
    注意到状态转移其实只跟奇偶有关
    则可以定义成dp[u][0/1][0/1]表示假定在u的祖先中给了第一棵树的点数的奇偶,给了第二颗树的点数的奇偶,子树u能获得的最大价值。
    于是可以写成这样 (dp[u][0/1][0/1])
    (egin{equation} dp[u][i][j] = max egin{cases} (sum_{v epsilon child(u)} dp[v][i xor 1][j])+(i==0?1:-1)*area[u] \ (sum_{vepsilon child(u)}dp[v][i][j xor 1])+(j==0?1:-1)*area[u] end{cases} end{equation})

    • 分析一下,由于每个点贡献是它在树中的深度有关,如果我们不假定它的祖先的状态的话,那么就无法从下到上做计算的。算出子树的值转移给祖先,这样最后的答案就是dp[root][0][0] (树根没有祖先当然是0,0)
    #include<bits/stdc++.h>
    #include<ext/pb_ds/assoc_container.hpp>
    #define LL long long
    #define P pair<string,int>
    #define ls(i) seg[i].lc
    #define rs(i) seg[i].rc
    using namespace std;
    using namespace __gnu_pbds;
    const int N = 1e3 + 10;
    const double PI = acos(-1.0);
    vector<int> G[N];
    int vis[N];
    long double area[N];
    struct circle{
        int x,y,r;
        circle(){};
        bool operator<(const circle &rhs)const{
            if(r != rhs.r) return r < rhs.r;
            if(x != rhs.x) return x < rhs.x;
            return y < rhs.y;
        }
    }c[N];
    LL sqr(int x){
        return 1LL * x * x;
    }
    bool contain(int a,int b){
        return  sqr(c[a].x - c[b].x) + sqr(c[a].y - c[b].y) <= sqr(c[a].r - c[b].r);
    }
    double dp[N][2][2];
    void dfs(int u){
        double s[2][2] = {0};
        for(int i = 0;i < G[u].size();i++){
            int v = G[u][i];
            dfs(v);
            for(int j = 0;j < 2;j++)
                for(int k = 0;k < 2;k++)
                    s[j][k] += dp[v][j][k];
        }
        for(int i = 0;i < 2;i++)
            for(int j = 0;j < 2;j++){
             dp[u][i][j] = max(s[i^1][j]+(i==0?1:-1)*area[u],s[i][j^1]+(j==0?1:-1)*area[u]);
        }
    }
    int main()
    {
        int n;
        cin>>n;
        for(int i = 1;i <= n;i++) scanf("%d%d%d",&c[i].x,&c[i].y,&c[i].r);
        sort(c+1,c+n+1);
        for(int i =1 ;i <= n;i++) vis[i] = 0;
        for(int i = 1;i <= n;i++){
            area[i] = PI * c[i].r * c[i].r;
            for(int j = 1 ;j < i;j++){
                if(!vis[j] && contain(i,j)){
                    G[i].push_back(j);
                    vis[j]++;
                    assert(vis[j]==1);
                }
            }
        }
        double ans = 0;
        for(int i = 1;i <= n;i++) if(!vis[i]){
            dfs(i);
            ans += dp[i][0][0];
        }
        printf("%.10lf
    ",ans);
        return 0;
    }
  • 相关阅读:
    如何使用git提交代码
    Eclipse升级ADT
    android学习:android开发常见技巧设置,提高开发效率
    Apache -Common-lang包使用
    最全的常用正则表达式大全——包括校验数字、字符、一些特殊的需求等等
    Asp.Net MVC entity framework模型验证
    Activity启动模式
    http服务器交互get,put,post,delete等说明
    js模块化插件开发
    Android Asynchronous Http Client异步网络请求使用
  • 原文地址:https://www.cnblogs.com/jiachinzhao/p/6975188.html
Copyright © 2011-2022 走看看