BZOJ 3616 War

KD树+bitset

对于一个联盟s,设会打它的个数为t,那它对答案的贡献就是 (ntn)m

考虑怎么得到t。对于一个点,我们希望在它能打到的所有点上面打上这个点的标记。直接打标记不行,考虑用KD树即可。然后联盟里的所有点用bitset或起来即可得到所有能打到这个联盟的点。

题目保证了坐标随机,因此KD树的复杂度是期望 n 的。

#include<cmath>
#include<bitset>
#include<cstdio>
#include<algorithm>
#define N 35005
using namespace std;
namespace runzhe2000
{
    typedef double db;
    int n, m, k, nocnt, D, rebel[N*5], vis[N];
    struct point
    {
        int x, y, r, a, bel;
        bool operator < (const point &that) const {return D ? x < that.x : y < that.y;}
    }p[N];
    struct node{int id, next;}no[N*300];
    struct KDT{int x[2], y[2], list;}t[N*5];
    bitset<N> tmp, f[N];
    void build(int x, int l, int r, int d)
    {
        D = d; t[x].list = 0; if(l == r) {rebel[x] = p[l].bel; t[x].x[0] = t[x].x[1] = p[l].x, t[x].y[0] = t[x].y[1] = p[l].y; return;}
        int mid = (l+r)>>1; nth_element(p+l, p+mid, p+r+1); build(x<<1,l,mid,d^1); build(x<<1|1,mid+1,r,d^1);
        t[x].x[0] = min(t[x<<1].x[0], t[x<<1|1].x[0]); t[x].x[1] = max(t[x<<1].x[1], t[x<<1|1].x[1]);
        t[x].y[0] = min(t[x<<1].y[0], t[x<<1|1].y[0]); t[x].y[1] = max(t[x<<1].y[1], t[x<<1|1].y[1]);
    }
    int mabs(int x){return x<0?-x:x;}
    int msqr(int x){return x*x;}
    void query(int x, int l, int r, int d, int id)
    {
        if(max(p[id].x - t[x].x[0], t[x].x[1] - p[id].x) + max(p[id].y - t[x].y[0], t[x].y[1] - p[id].y) <= p[id].a
        || sqrt((db)(msqr(max(p[id].x - t[x].x[0], t[x].x[1] - p[id].x)) + msqr(max(p[id].y - t[x].y[0], t[x].y[1] - p[id].y)))) <= p[id].r)
            {no[++nocnt] = (node){id, t[x].list}, t[x].list = nocnt; return;}
        if(max(p[id].x-t[x].x[1],0)+max(t[x].x[0]-p[id].x,0)+max(p[id].y-t[x].y[1],0)+max(t[x].y[0]-p[id].y,0) > p[id].a
        && sqrt((db)(msqr(max(p[id].x-t[x].x[1],0)+max(t[x].x[0]-p[id].x,0))+msqr(max(p[id].y-t[x].y[1],0)+max(t[x].y[0]-p[id].y,0)))) > p[id].r)
            return;
        int mid = (l+r)>>1; query(x<<1,l,mid,d^1,id); query(x<<1|1,mid+1,r,d^1,id); 
    }
    void search(int x, int l, int r)
    {
        for(int i = t[x].list; i; i = no[i].next) tmp.set(no[i].id);
        if(l == r) f[rebel[x]] |= tmp;
        else {int mid = (l+r)>>1; search(x<<1,l,mid); search(x<<1|1,mid+1,r);}
        for(int i = t[x].list; i; i = no[i].next) tmp.reset(no[i].id);
    }
    db fpow(db a, int b)
    {
        db r = 1;
        for(; b; b>>=1)
        {
            if(b&1)r=r*a;
            a=a*a;
        }
        return r;
    }
    void main()
    {
        scanf("%d%d%d",&n,&m,&k);
        for(int i = 1; i <= n; i++)
            scanf("%d%d%d%d%d",&p[i].x,&p[i].y,&p[i].r,&p[i].a,&p[i].bel);
        build(1,1,n,0); for(int i = 1; i <= n; i++) query(1,1,n,0,i);
        tmp.reset(); search(1,1,n);
        for(int i = 1; i <= n; i++) f[p[i].bel].reset(i);
        db ans = 0;
        for(int i = 1; i <= k; i++)
            ans += fpow((db)(n-f[i].count())/n, m);
        printf("%.5lf\n",ans);
    }
}
int main()
{
    runzhe2000::main(); 
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值