HDU 5021 Revenge of kNN II 线段树

【题目大意】

有n个点,每个点都两个属性:坐标和价值。有m次询问,对一次询问,q,k,你首先要找到点q最近的k个点(不包括点q,如果边界有两个点都可以选,选择坐标小的)。然后求这k个点的平均价值,使 ans += 平均值,并把点q的价值改为这个平均值。最后输出ans。

【思路】

这个问题的关键是找到要最近的k个点,具体是哪k个。如果搞定了这个问题,剩下的就是线段树的基础操作了。如果按坐标排序后,可以使用二分去找k个点所属区间。但是这题直接二分是有点麻烦的。我采用的是二分距离,找到最近的k个点中,最远的那个点距离;之后再特判一下边界就行了,这种做法比较好写。


//#pragma comment(linker, "/STACK:102400000,102400000")
#include<cstdio>
#include<cstring>
#include<vector>
#include<queue>
#include<cmath>
#include<cctype>
#include<string>
#include<algorithm>
#include<iostream>
#include<ctime>
#include<map>
#include<set>
using namespace std;
#define MP(x,y) make_pair((x),(y))
#define PB(x) push_back(x)
typedef __int64 LL;
//typedef unsigned __int64 ULL;
/* ****************** */
const int INF = 1000111222;
const double INFF = 1e100;
const double eps = 1e-8;
const int mod = 1000000007;
const int NN = 100009;
const int MM = 400010;
/* ****************** */

struct TR
{
    int l,r;
    double sum;
    int mid()
    {
        return (l+r)>>1;
    }
}tr[NN*4];

struct node
{
    int x,id;
    double v;
    bool operator<(const node &tt)const
    {
        return x < tt.x;
    }
}a[NN];
int tf[NN];
int id[NN];
int idd[NN];


void push_up(int R)
{
    tr[R].sum = tr[R<<1].sum + tr[R<<1|1].sum;
}
void build(int l,int r,int R)
{
    tr[R].l = l;
    tr[R].r = r;
    if(l==r)
    {
        tr[R].sum = a[tr[R].l].v;
        return ;
    }
    int mid = tr[R].mid();
    build(l,mid,R<<1);
    build(mid+1,r,R<<1|1);
    push_up(R);
}
double query(int l,int r,int R)
{
    if(l<=tr[R].l && tr[R].r<=r)
        return tr[R].sum;
    double ans = 0.0;
    int mid = tr[R].mid();
    if(l<=mid)
        ans += query(l,r,R<<1);
    if(r>=mid+1)
        ans += query(l,r,R<<1|1);
    return ans;
}
void update(int x,int R,double col)
{
    if(tr[R].l == tr[R].r)
    {
        tr[R].sum = col;
        return;
    }
    int mid = tr[R].mid();
    if(x<=mid)
        update(x,R<<1,col);
    else
        update(x,R<<1|1,col);
    push_up(R);
}

int find(int n,int l,int r)
{
    int ans =  upper_bound(tf+1,tf+1+n,r) - lower_bound(tf+1,tf+1+n,l);
    return ans;
}

void solve(int n,int m)
{
    int x,k,l,r,st,mid,t;
    double sum, ans = 0.0;
    while(m--)
    {
        scanf("%d%d",&x,&k);
        x = id[x];

        l = 0;
        r = 1000000000;
        while (l + 1 < r)
        {
            mid = (l + r) >> 1;
            t = find(n,a[x].x-mid,a[x].x+mid);
            if(t >= k+1)
                r = mid;
            else
                l = mid;
        }

      //  printf("r==%d\n",r);

        st = lower_bound(tf+1,tf+1+n,a[x].x-r) - tf;

        if(st+k+1 <= n && tf[st+k+1]-tf[x] == tf[x]-tf[st] && idd[st+k+1] < idd[st])
            st++;

     //   printf("[] == %d %d\nx==%d\n",st,st+k,x);

        sum = query(st,st+k,1);

     //   cout<<"sum=="<<sum<<endl;

        sum -= a[x].v;
        sum /= (k+0.0);

      //  cout<<"update=="<<sum<<endl;

        ans += sum;

        update(x,1,sum);
        a[x].v = sum;
    }

  //  cout<<"ans=="<<tr[1].sum<<endl;

    printf("%.3lf\n",ans);
}

int main()
{
    int cas,i,t,n,m;
    scanf("%d",&cas);
    while(cas--)
    {
        scanf("%d%d",&n,&m);
        for(i=1;i<=n;i++)
        {
            scanf("%d%d",&a[i].x,&t);
            a[i].v = t;
            a[i].id = i;
        }

        sort(a+1,a+1+n);

        for(i=1;i<=n;i++)
        {
            tf[i] = a[i].x;
            id[a[i].id] = i;
            idd[i] = a[i].id;
        }

//        for(i=1;i<=n;i++)
//        {
//            printf("%d %d ???\n",a[i].x,a[i].id);
//          //  printf("-> %d\n",id[i]);
//        }

        build(1,n,1);

        solve(n,m);
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值