zoukankan      html  css  js  c++  java
  • DTOJ #2768. 圆圈游戏(circle)

    【题目描述】

    在无聊的时候,小 $K$ 和小 $H$ 会在纸上玩这样一个游戏。

    我们可以将纸看做一个平面直角坐标系。小 $H$ 会先在上面画出 $n$ 个圆,并把每个圆的圆心以及半径都告诉小 $K$。小 $H$ 画的 $n$ 个圆中,任意两个圆不会出现相交或相切的情况。小 $K$ 需要做的就是从这 $n$ 个圆中选出若干个圆,使得选出的任意一个圆都不被另一个选出的圆包含。游戏的目标就是要选出尽量多的圆。

    游戏一次一次进行着,小 $K$ 已经对游戏的规则感到了厌倦,所以他决定修改游戏的规则。对于第 $i$ 个圆,我们定义它的价值为 $w_i$ 。新的游戏目标是使得选出的圆价值和最大(不一定数量最多)。但是圆圈可能很多,或者圆圈的分布非常奇怪,或者小 $K$ 还有别的事情要做。所以他只好拜托你来帮他求出这个最大值了。

    【输入格式】

    第一行一个整数 $n$ 表示圆圈的个数。

    接下来 $n$ 行每行 $4$ 个整数 $x_i,y_i,r_i$ 和 $w_i$ ,分别表示第 $i$ 个圆的圆心横坐标、纵坐标、半径,和价值。

    【输出格式】

    输出一行,包含一个整数,代表选出的圆的最大价值。

    【样例】

    样例输入
    3
    3 4 2 3
    6 4 7 5
    9 4 1 4


    样例输出
    7

    样例解释

    如果选择价值最大的圆 $2$ ,可以获得的价值和为 $5$。如果选择圆 $1$ 和圆 $3$,虽然它们的单个价值都不是最大的,但价值和可以达到 $3+ 4 = 7$。

    【数据范围与提示】

    测试点编号 $n=$
    $1$ $1$
    $2$ $2$
    $3$ $3$
    $4$ $4$
    $5$ $8$
    $6$ $12$
    $7$ $16$
    $8,9$ $1000$
    $10,11$ $2000$
    $12,13$ $3000$
    $14,15$ $5000$
    $16,17$ $60000$
    $18,19$ $70000$
    $20,21$ $80000$
    $22,23$ $90000$
    $24,25$ $100000$

    对于全部测试数据 $1 le x_i,y_i,r_i le 10^8,1 le w_i le 1000 $。

    保证不存在相交或相切的两个圆。

    【题解】

    看到不互相包含的圆,显然是最大权独立集问题。又注意到每个圆一定被另一个最小圆包含,因此圆与圆之间由包含关系构成树形结构。若能够建出树则可以树形 $dp$。

    考虑 $60$ 分做法。按 $r$ 从小到大扫一遍,对于每个圆找到最小的包含它的圆,直接建边跑树形 $dp$ 即可。效率 $O(n^2)$。

    考虑如何优化建树。首先可以反过来建边,从外往里。同样从小到大插入每个圆。由于圆与圆不相交,圆心在当前插入圆内的圆一定被当前圆包含。直接连边然后删除即可。

    由于是二维限制,用 $kd-tree$ 或二维线段树均可。

    另一种做法是用扫描线,对于每个圆拆成上下两半,左右两个端点视为插入与删除的扫描线。用 $set,splay$ 等数据结构维护每个半圆的位置上下关系。查询时查询上半圆前驱与下半圆后继即可。

    【代码】

    #include<bits/stdc++.h>
    inline int read ( void )
    {
    	int x=0;char ch;bool f=true;
    	while ( !isdigit(ch=getchar()) ) if ( ch=='-' ) f=false;
    	for ( x=ch^48;isdigit(ch=getchar()); ) x=(x<<1)+(x<<3)+(ch^48);
    	return f ? x : -x ;
    }
    int f[100010],n,tot,root;
    struct tree { int ch[4],tot,id; } t[10000010];
    struct Circle { long long x,y,r;int w; } C[100010];
    std::vector<int> E[100010];
    const int inf=1000000000;
    const int L=-inf,R=inf;
    inline bool inside ( int l,int r,int d,int u,int x,int y,long long R )
    {
    	long long dx=std::max(std::max(x-r,l-x),0),dy=std::max(std::max(y-u,d-y),0);
    	return (dx*dx+dy*dy<=R*R);
    }
    inline void pushup ( int k ) { t[k].tot=t[t[k].ch[0]].tot+t[t[k].ch[1]].tot+t[t[k].ch[2]].tot+t[t[k].ch[3]].tot; }
    inline int query ( int l,int r,int d,int u,int k,int x,int y,int R )
    {
    	if ( !t[k].tot or !inside(l,r,d,u,x,y,R) ) return 0;
    	if ( l==r and d==u ) { t[k].tot=0;return f[t[k].id]; }
    	int mid1=(l+r)>>1,mid2=(d+u)>>1,res=0;
    	res+=query(l,mid1,d,mid2,t[k].ch[0],x,y,R);
    	res+=query(l,mid1,mid2+1,u,t[k].ch[1],x,y,R);
    	res+=query(mid1+1,r,d,mid2,t[k].ch[2],x,y,R);
    	res+=query(mid1+1,r,mid2+1,u,t[k].ch[3],x,y,R);
    	pushup(k);
    	return res;
    }
    inline void modify ( int l,int r,int d,int u,int &k,int x,int y,int i )
    {
    	if ( !k ) k=++tot;
    	if ( l==r and d==u ) { t[k].tot=1;t[k].id=i;return; }
    	int mid1=(l+r)>>1,mid2=(d+u)>>1;
    	if ( x<=mid1 and y<=mid2 ) modify(l,mid1,d,mid2,t[k].ch[0],x,y,i);
    	if ( x<=mid1 and y>mid2 ) modify(l,mid1,mid2+1,u,t[k].ch[1],x,y,i);
    	if ( x>mid1 and y<=mid2 ) modify(mid1+1,r,d,mid2,t[k].ch[2],x,y,i);
    	if ( x>mid1 and y>mid2 ) modify(mid1+1,r,mid2+1,u,t[k].ch[3],x,y,i);
    	pushup(k);
    }
    signed main()
    {
    	n=read();
    	for ( int i=1;i<=n;i++ ) C[i].x=read(),C[i].y=read(),C[i].r=read(),C[i].w=read();
    	++n;C[n].r=inf;std::sort(C+1,C+n+1,[&](const Circle &c1,const Circle &c2){return c1.r<c2.r;});
    	for ( int i=1;i<=n;i++ ) f[i]=std::max(query(L,R,L,R,root,C[i].x,C[i].y,C[i].r),C[i].w),modify(L,R,L,R,root,C[i].x,C[i].y,i);
    	return !printf("%d
    ",f[n]);
    }
    

      

  • 相关阅读:
    spark 脚本示例
    R树的应用
    将博客搬至CSDN
    select
    注册页面的验证码的实现
    web项目.注册及登陆
    eclipse web 项目中遇到的问题总结
    Apache与Tomcat
    关于MVC整理
    JDBC
  • 原文地址:https://www.cnblogs.com/RenSheYu/p/11311752.html
Copyright © 2011-2022 走看看