BZOJ 2888 资源运输(启发式合并LCT)

【题目链接】 http://www.lydsy.com/JudgeOnline/problem.php?id=2888

【题目大意】

  不断加边,问每个连通块的重心到其它点的距离和的和

【题解】

  启发式合并LCT,通过维护等差数列的首项和公差
  来实现保存子树内所有节点到这个节点的距离之和。

【代码】

#include <cstdio>
#include <algorithm>
#include <cstring>
using namespace std;
const int N=40010;
namespace Heuristic_LCT{
    int ans,g[N],v[N<<1],nxt[N<<1],ed;
    int f[N],son[N][2],val[N],tag[N],sum[N],ts[N],td[N],size[N],tmp[N];
    bool isroot(int x){return !f[x]||son[f[x]][0]!=x&&son[f[x]][1]!=x;}
    void add1(int x,int p){if(!x)return;val[x]+=p;tag[x]+=p;}
    void add2(int x,int s,int d){if(!x)return;sum[x]+=s+size[son[x][1]]*d;ts[x]+=s;td[x]+=d;}
    void pb(int x){
        if(tag[x]){
            add1(son[x][0],tag[x]);
            add1(son[x][1],tag[x]);
            tag[x]=0;
        }
        if(td[x]){
            add2(son[x][0],ts[x]+(size[son[x][1]]+1)*td[x],td[x]);
            add2(son[x][1],ts[x],td[x]);
            ts[x]=td[x]=0;
        }
    }
    void up(int x){size[x]=size[son[x][0]]+size[son[x][1]]+1;}
    void rotate(int x){
        int y=f[x],w=son[y][1]==x;
        son[y][w]=son[x][w^1];
        if(son[x][w^1])f[son[x][w^1]]=y;
        if(f[y]){
            int z=f[y];
            if(son[z][0]==y)son[z][0]=x;else if(son[z][1]==y)son[z][1]=x;
        }f[x]=f[y];f[y]=x;son[x][w^1]=y;up(y);
    }
    void splay(int x){
        int s=1,i=x,y;tmp[1]=i;
        while(!isroot(i))tmp[++s]=i=f[i];
        while(s)pb(tmp[s--]);
        while(!isroot(x)){
            y=f[x];
            if(!isroot(y)){if((son[f[y]][0]==y)^(son[y][0]==x))rotate(x);else rotate(y);}
            rotate(x);
        }up(x);
    }
    void access(int x){for(int y=0;x;y=x,x=f[x])splay(x),son[x][1]=y,up(x);}
    int root(int x){access(x);splay(x);while(son[x][0])x=son[x][0];return x;}
    void addleaf(int x,int y){
        f[y]=x,son[y][0]=son[y][1]=val[y]=tag[y]=sum[y]=ts[y]=td[y]=0,size[y]=1;
        x=root(x),access(y),splay(x),add1(x,1),add2(x,0,1);
        for(y=son[x][1];son[y][0];y=son[y][0]);splay(y);
        int vx=val[x],vy=val[y];
        if(vy*2>vx){
            val[y]=vx,val[x]-=vy;
            sum[x]-=sum[y]+vy,sum[y]+=sum[x]+vx-vy;
            access(y),splay(x),son[x][0]=y,son[x][1]=0;
        }
    }
    void dfs(int x,int y){
        addleaf(y,x);
        for(int i=g[x];i;i=nxt[i])if(v[i]!=y)dfs(v[i],x);
    }
    void addedge(int x,int y){v[++ed]=y;nxt[ed]=g[x];g[x]=ed;}
    void link(int x,int y){
        int X=root(x),Y=root(y);
        ans-=sum[X]+sum[Y];
        if(val[X]<val[Y])swap(x,y);
        dfs(y,x),addedge(x,y),addedge(y,x);
        ans+=sum[root(x)];
    }
    void Initialize(){
        ans=0;
        memset(f,0,sizeof(f));
        memset(son,0,sizeof(son));
        memset(ts,0,sizeof(ts));
        memset(td,0,sizeof(td));
        memset(tag,0,sizeof(tag));
    }
}
int n,m,x,y;
char op[5];
int main(){
    using namespace Heuristic_LCT;
    Initialize();
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)val[i]=size[i]=1;
    while(m--){
        scanf("%s",op);
        if(op[0]==‘A‘)scanf("%d%d",&x,&y),link(x,y);
        if(op[0]==‘Q‘)printf("%d\n",ans);
    }return 0;
}
时间: 2024-10-03 00:00:54

BZOJ 2888 资源运输(启发式合并LCT)的相关文章

BZOJ 2888: 资源运输

Description 加边,询问连通块中所有点到重心的距离. Solution LCT. http://www.cnblogs.com/clrs97/p/4776809.html 一开始没想到怎么合并两颗树时候计算贡献... Code /************************************************************** Problem: 2888 User: BeiYu Language: C++ Result: Accepted Time:1560

[bzoj3123] [SDOI2013]森林 主席树+启发式合并+LCT

Description Input 第一行包含一个正整数testcase,表示当前测试数据的测试点编号.保证1≤testcase≤20. 第二行包含三个整数N,M,T,分别表示节点数.初始边数.操作数.第三行包含N个非负整数表示 N个节点上的权值. 接下来 M行,每行包含两个整数x和 y,表示初始的时候,点x和点y 之间有一条无向边, 接下来 T行,每行描述一个操作,格式为"Q x y k"或者"L x y ",其含义见题目描述部分. Output 对于每一个第一类

【BZOJ-2888】资源运输 LCT + 启发式合并

2888: 资源运输 Time Limit: 10 Sec  Memory Limit: 128 MBSubmit: 63  Solved: 33[Submit][Status][Discuss] Description 小Y盯上了最近发行的即时战略游戏——ResourceTransport.但在前往通关之路的道路上,一个小游戏挡住了小Y的步伐.“国家的本质是生产与收集资源”是整款游戏的核心理念,这个小游戏也不例外.简单的说,用户需要管理一个国家,使其繁荣富强. 一个国家含有N个城市,游戏开始时

BZOJ.3510.首都(LCT 启发式合并 树的重心)

题目链接 BZOJ 洛谷 详见这. 求所有点到某个点距离和最短,即求树的重心.考虑如何动态维护. 两棵子树合并后的重心一定在两棵树的重心之间那条链上,所以在合并的时候用启发式合并,每合并一个点检查sz[]大的那棵子树的重心(记为root)最大子树的sz[] * 2是否>n: 若>n,则向fa移动一次(先把合并点Splay到根).重心还一定是在sz[]大的那棵子树中,且移动次数不会超过sz[]小的子树的点数(所以总移动次数不会超过O(n)?). 复杂度 \(O(nlog^2n)\) 具体实现..

BZOJ 2733: [HNOI2012]永无乡(treap + 启发式合并 + 并查集)

不难...treap + 启发式合并 + 并查集 搞搞就行了 ---------------------------------------------------------------------------------------- #include<cstdio> #include<cstring> #include<algorithm> #include<iostream> #define rep(i, n) for(int i = 0; i &l

[BZOJ 1483][HNOI 2009]梦幻补丁(有序表启发式合并)

题目:http://www.lydsy.com:808/JudgeOnline/problem.php?id=1483 分析: 先将不同的颜色的出现位置从小到大用几条链表串起来,然后统计一下答案 对于每次修改,修改一下答案即可,修改之后需要将两个颜色的链表合并就行了,但感觉似乎会TLE? 以下摘录与Hzwer的blog: 1:将两个队列合并,有若干队列,总长度为n,直接合并,最坏O(N), 2:启发式合并呢? 每次我们把短的合并到长的上面去,O(短的长度) 咋看之下没有多大区别, 下面让我们看看

BZOJ 1483 [HNOI2009]梦幻布丁 链式前向星+启发式合并

题意: 题意很清晰就是没有数据范围- -! 直到现在我都不知道数据范围有多大! 刚开始给定一个序列. 多次操作. 第一种是把值为x的点的值改为y 第二种是询问当前有多少段权值全部相同. 解析: 显然暴力合并的复杂度是O(nm)的,不可取. 所以来考虑黑科技. 如果我们改一改合并的方式. 每一次把短的接在长的上面,那么最少也是使短的长度变为原来的二倍. 所以这种操作如果不卡的话大概是log次? 但是要是卡的话好像挺难? 如果使合并次数增加那么每一次合并的元素就越来越少,所以应该是卡不住的. 所以我

【BZOJ 3123】 [Sdoi2013]森林 主席树启发式合并

我们直接按父子关系建主席树,然后记录倍增方便以后求LCA,同时用并查集维护根节点,而且还要记录根节点对应的size,用来对其启发式合并,然后每当我们合并的时候我们都要暴力拆小的一部分重复以上部分,总时间复杂度为O(n*log),因为每个的节点只会作为小的部分合并,因此他所在的一小部分至少变大2倍,对于每一个节点他作为被合并方最多log次,因此其复杂度为O(n*log),而这个是绝对跑不满还差很多的,我们视他为无常数就好了,当然这一切都是建立在无拆分操作的基础之上,只要有了拆分启发式合并的复杂度就

BZOJ 2754 SCOI2012 喵星球上的点名 fail树+set启发式合并

题目大意:给定n个目标串和m个模式串,问这m个模式串每个在多少个目标串中出现过,以及n个目标串每个以最多多少个模式串为子串 我错了--就算用fail树+set启发式合并也优化不到O(nlog^2n)--这题的数据范围相当无解啊 首先将所有名字和点名的字符串全都插进AC自动机 将每个点上开一个set记录这个点是哪些喵星人的名字的前缀 然后建立fail树 沿着fail树从下到上启发式合并 每合并完一个点 如果这个点是某次点名的字符串 那么这次点名点到的喵星人就是这个点的set中的所有元素 统计答案即