F - Disharmony Trees(树状数组)

F - Disharmony Trees

HDU - 3015

题意:

​ 对于n棵树,给出所在位置和高度,根据给出的规则算出每棵树的位置等级 x l e v xlev xlev和高度等级 h l e v hlev hlev

然后,定义f=两树之间的位置的那估计的绝对值,s=两树中最小的高度等级,求所有树之间f*s和。

思路:

​ 预处理把所有的树重新构造距离和高度熟属性,我们让所有树按照树的高度从高到低排序,那么每次求当前这个树与前面每个树的 Disharmony Value 即可,因为高度是从高到低,所以与前面每颗数计算的min(H1,H2)都为自己的高度。且求Disharmony Value操作可以优化到logn级别,假设前面的树的距离为aX,高度为aH,当前树的距离为X,高度为H,那么 a b s ( a X − X ) ∗ m i n ( a H , H ) = a b s ( a X − X ) ∗ H abs(aX-X)*min(aH,H)=abs(aX-X)*H abs(aXX)min(aH,H)=abs(aXX)H

  • 如果 a X > = X aX>=X aX>=X a b s ( a X − X ) ∗ H = a X ∗ H − X ∗ H ​ abs(aX-X)*H=aX*H-X*H​ abs(aXX)H=aXHXH
  • 否则: a b s ( a X − X ) ∗ H = X ∗ H − a X ∗ H abs(aX-X)*H=X*H-aX*H abs(aXX)H=XHaXH

那么我们可以统计当前所有的树满足 x &gt; = X x&gt;=X x>=X 的所有距离和** 以及个数,以及当前满足 x &lt; X x&lt;X x<X的所有距离和 以及个数 即可。然后加到答案上。

即用树状数组维护区间个数个区间和即可。

#include<bits/stdc++.h>
#define mset(a,b) memset(a,b,sizeof(a))
using namespace std;
typedef long long ll;
const ll MAXN=1e5+20;
ll MAX=1e5+1;
ll sx[MAXN],sh[MAXN];//数据的等级
ll X[MAXN],H[MAXN];//数据
ll n;
struct Node
{
    ll x,h;
} node[MAXN];
bool cmp(Node a,Node b)
{
    return a.h<b.h;
}
ll getxth(ll x)
{
    ll th=lower_bound(X,X+1+n,x)-X;
    return sx[th];
}
ll gethth(ll h)
{
    ll th=lower_bound(H,H+1+n,h)-H;
    return sh[th];
}
ll btt[MAXN],bts[MAXN];//对应节点出现的个数  和出现的X的和
ll book[MAXN],SUM,TOT;
ll lowbit(ll k)
{
    return k&-k;
}
void modify(ll k)
{
    ll val=k;
    while(k<=MAX)
    {
        btt[k]++;
        bts[k]+=val;
        k+=lowbit(k);
    }
}
pair<ll,ll> getinfo(ll k)//1~k 的个数为tot,他们的和为sum
{
    ll tot=0,sum=0;
    while(k>0)
    {
        tot+=btt[k];
        sum+=bts[k];
        k-=lowbit(k);
    }
    return make_pair(tot,sum);
}
void calc(ll l,ll r,ll &tot,ll &sum)
{
    pair<ll,ll> PA,PB;
    PB=getinfo(r);
    PA=getinfo(l-1);
    tot=PB.first-PA.first;
    sum=PB.second-PA.second;
}
int main()
{
    while(~scanf("%lld",&n))
    {
        X[0]=H[0]=0;
        sx[0]=sh[0]=0;
        MAX=-1;
        for(ll i=1; i<=n; ++i)
        {
            scanf("%lld%lld",X+i,H+i);
            node[i].x=X[i];
            node[i].h=H[i];
        }
        sort(X,X+n+1);
        sort(H,H+n+1);
        sort(node+1,node+1+n,cmp);
        for(ll i=1; i<=n; ++i)
        {
            if(X[i]==X[i-1])
                sx[i]=sx[i-1];
            else
                sx[i]=i;
            if(H[i]==H[i-1])
                sh[i]=sh[i-1];
            else
                sh[i]=i;
            MAX=max(MAX,sx[i]);
        }
        mset(btt,0);
        mset(bts,0);
        mset(book,0);
        ll ans=0;
        SUM=TOT=0;
        for(ll i=n; i>=1; --i) //h从大到小遍历, 找到当前比他小 和比他大的个数
        {
            ll xlev=getxth(node[i].x),hlev=gethth(node[i].h);
            TOT++;
            SUM+=xlev;
            modify(xlev);
            ll lstot,lssum,uptot,upsum;
            calc(1,xlev,lstot,lssum);
            uptot=TOT-lstot;
            upsum=SUM-lssum;
            ll mid=((lstot-uptot)*xlev+upsum-lssum)*hlev;
            ans+=((lstot-uptot)*xlev+upsum-lssum)*hlev;
        }
        printf("%lld\n",ans);
    }
    return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值