whuProblem 1605 - Distance on Tree

链接:http://acm.whu.edu.cn/land/problem/detail?problem_id=1605

题意:给定n,k,q表示一棵n个节点的树,i的父亲是i/k并且i到父亲的距离为i。给定q个询问:[l,r],求编号在l~r之间的点两两之间的距离的总和。

分析:设d[x]为x到根的距离,那么我们能将两两之间的距离dis[a,b]=d[a]+d[b]-2*d[lca]。我们再考虑一下当区间从[,r]变为[l,r+1]是的答案变化。显然是增加了r+1的贡献,我们利用dis[i,r+1]这个公式可以快速算出前两项的值,但是-2*d[lca]是不好直接求得的。那么我们对于固定一个点r+1来枚举所有的lca,这里时间复杂度是logk(n)的,那么我们只要对于每一个枚举的lca只要知道有多少节点刚好是这个就行了,我们只要加一个统计值g[i]表示i子树下在[l,r]区间的节点数即可。这样的话我们就能用莫队做出来啦。详见代码。O(n*sqrt(n)*logk(n))

代码:

#include<map>
#include<set>
#include<cmath>
#include<queue>
#include<bitset>
#include<math.h>
#include<cstdio>
#include<vector>
#include<string>
#include<cstring>
#include<iostream>
#include<algorithm>
#pragma comment(linker, "/STACK:102400000,102400000")
using namespace std;
const int N=10010;
const int MAX=100000000;
const int mod=100000000;
const int MOD1=1000000007;
const int MOD2=1000000009;
const double EPS=0.00000001;
typedef long long ll;
const ll MOD=998244353;
const ll INF=10000000010;
typedef double db;
typedef unsigned long long ull;
struct node {
    int l,r,id,cl;
}a[N];
int cmp(node x,node y) {
    if (x.cl!=y.cl) return x.cl<y.cl;
    return x.r<y.r;
}
int n,k,L,R,g[N];
ll tot,sum,d[N],ans[N];
ll get(int x) {
    ll ret=(R-L+1)*d[x]+sum;
    int gg=0;
    while (x) {
        ret-=2*(g[x]-gg)*d[x];
        gg=g[x];x/=k;
    }
    return ret;
}
int main()
{
    int i,q,clo,w;
    while (scanf("%d%d%d", &n, &k, &q)!=EOF) {
        clo=(int)sqrt(n);
        for (i=1;i<=q;i++) {
            scanf("%d%d", &a[i].l, &a[i].r);
            a[i].cl=a[i].l/clo;a[i].id=i;
        }
        sort(a+1,a+q+1,cmp);
        d[0]=0ll;
        for (i=1;i<n;i++) d[i]=d[i/k]+i;
        sum=tot=L=R=0;
        memset(g,0,sizeof(g));
        for (i=1;i<=q;i++) {
            while (R<a[i].r) {
                tot+=get(R+1);
                R++;w=R;sum+=d[R];
                while (w) { g[w]++;w/=k; }
            }
            while (L<a[i].l) {
                w=L;L++;
                while (w) { g[w]--;w/=k; }
                sum-=d[L-1];tot-=get(L-1);
            }
            while (L>a[i].l) {
                tot+=get(L-1);
                L--;w=L;sum+=d[L];
                while (w) { g[w]++;w/=k; }
            }
            while (R>a[i].r) {
                w=R;R--;
                while (w) { g[w]--;w/=k; }
                sum-=d[R+1];tot-=get(R+1);
            }
            ans[a[i].id]=tot;
        }
        for (i=1;i<=q;i++) printf("%lld\n", ans[i]);
    }
    return 0;
}


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值