[莫队算法] WHU Problem 1605 - Distance on Tree

来源:互联网 发布:数据库设计第三范式 编辑:程序博客网 时间:2024/06/04 01:05

Problem 1605 - Distance on Tree
题意:
一棵树有N个节点,标号从0开始,再给一个常数k,0号点以外的任意节点i和i/k号点有一条边,边权是i。
每次询问qurey[l,r],要求输出ans=r1i=lrj=i+1dis(i,j),其中dis(i,j)表示从ij的权值和。
题解:
显然0号点是树根。
对于dis(i,j),用len[i]表示从0号点到i号点的权值和,那么dis(i,j)=len[i]+len[j]2len[lca(i,j)]

然后假设我们已经求得query(i,j),那么query(i1,j)=query(i,j)+jk=i(len[i1]+len[k]2len[lca(i1,k)]query(i,j+1)

其中两项容易算出:
jk=ilen[i1]=(ji+1)len[i1],可直接算。
jk=ilen[k],处理query(i,j)的时候可以维护。

然后就是要快速算出jk=i2len[lca(i1,k)],一种方法是维护一段标记,对于节点k[i,j],就把k到根节点的路径上所有边的标记值都累加上k,容易看出这个过程在处理query(i,j)的时候可以维护。
要算jk=i2len[lca(i1,k)]的时候,就从i1不断往根走,同时不断加上标记值,想想就知道正确性了。
因为这一维护过程,[l,r][l1,r],[l,r+1]的复杂度是logkN,总复杂度是O(Nsqrt(N)logkN)
代码里面用len维护(ji+1),sumdis维护jk=ilen[k],mk数组作为标记数组维护jk=i2len[lca(i1,k)]

#include<stdio.h>#include<math.h>#include<algorithm>typedef long long ll;const int N = 10005;const int MAXQ = 10005;struct qry{    int l, r, id, bk;    bool operator < (const qry &a)const{        if(bk != a.bk) return bk < a.bk;        return r < a.r;    }}Q[MAXQ];int n, k, q, f[N];int unit;ll mk[N], dis[N];ll ans, sumdis, len;ll anss[N];void add(int x){    ans += len*dis[x] + sumdis;    sumdis += dis[x], len += 1;    while(x){        ans -= 2*mk[x];        mk[x] += x;        x = f[x];    }}void del(int x){    sumdis -= dis[x], len -= 1;    ans -= len*dis[x] + sumdis;    while(x){        mk[x] -= x;        ans += 2*mk[x];        x = f[x];    }}int main(){    while(scanf("%d%d%d", &n, &k, &q) != EOF){        ans = sumdis = 0, len = 0;        for(int i = 1; i < n; ++i){            f[i] = i/k;            dis[i] = dis[f[i]]+i;            mk[i] = 0;        }        unit = int(sqrt(n)+0.5);        for(int i = 1; i <= q; ++i){            Q[i].id = i;            scanf("%d%d", &Q[i].l, &Q[i].r);            Q[i].bk = Q[i].l/unit;        }        std::sort(Q+1, Q+q+1);        int l = 1, r = 0;        for(int i = 1; i <= q; ++i){            while(r < Q[i].r) add(++r);            while(r > Q[i].r) del(r--);            while(l < Q[i].l) del(l++);            while(l > Q[i].l) add(--l);            anss[Q[i].id] = ans;        }        for(int i = 1; i <= q; ++i) printf("%lld\n", anss[i]);    }}
0 0
原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 安全瓶盖坏了怎么办 刚买面霜打不开怎么办 可乐瓶盖鼓起来怎么办 暖壶塞子吸住了怎么办 茶兀瓶盖打不开怎么办 水杯盖太紧了拧不开怎么办 矿泉水瓶盖拧不开了怎么办 弩弦用手拉不上怎么办 茅台酒瓶口漏酒怎么办 化妆品盖子丢了怎么办 化妆品盖子碎了怎么办 自制水泵压力小怎么办 大学数学不会做怎么办 下雪了怎么办教案幼儿园小班 下水道被混凝土堵塞怎么办 日本足贴丢了胶布怎么办 牙齿被可乐腐蚀怎么办 三十岁满嘴无牙怎么办 水乳盖子打不开怎么办 蜂蜜罐子打不开了怎么办 蜂蜜盖子第二次拧不开怎么办 玻璃杯子拧不开盖子怎么办 玻璃杯水杯盖子拧不开怎么办 鞋子蝴蝶结掉了怎么办 蝴蝶翅膀受伤了怎么办 手被割了个口子怎么办 致炫方向盘重怎么办 黑檀7打不透怎么办 乒乓球底板太轻怎么办 狙击精英4卡怎么办 鼠标点一下变两下怎么办 工程干完不给钱怎么办 屋里有大蛾子怎么办 房间很多小飞虫怎么办 雷蛇键盘失灵怎么办 xp驱动 不支持win10怎么办 阿提拉全面战争统治度太低怎么办 微信号变成wxid怎么办 ipv4 ipv6未连接怎么办 土豆丝粘锅怎么办还面 土豆丝容易碎怎么办