【Splay】【启发式合并】hdu6133 Army Formations

题意:给你一颗树,每个结点的儿子数不超过2。每个结点有一个权值,一个结点的代价被定义为将其子树中所有结点的权值放入数组排序后,每个权值乘以其下标的和。让你计算所有结点的代价。

二叉树的条件没有用到。

每个结点开一个Splay,从叶子往上启发式合并上去,可以先bfs一遍确定合并顺序。每一次将Splay大小较小的结点的权值全提取出来塞到较大的里面。

由于权值可能重复出现,所以每个结点记个cnt。

答案统计的时候,就将那个刚塞进去的旋到根,然后答案加上左子树的权值和,再加上(右子树的权值的个数+该结点的cnt)*该结点的权值。

然后将较大儿子的Splay的根丢给父亲。

不必要进行内存回收,通过计算,所开的空间只要达到nlogn即可,实际上100w足够了。

#include<cstdio>
#include<set>
#include<queue>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long ll;
const int maxn=1000000;
ll sum[maxn];
int fa[maxn],val[maxn],c[maxn][2],root,tot,siz[maxn],cnt[maxn];
void Maintain(int x)
{
    siz[x]=siz[c[x][0]]+siz[c[x][1]]+cnt[x];
    sum[x]=sum[c[x][0]]+sum[c[x][1]]+(ll)val[x]*(ll)cnt[x];
}
void NewNode(int &x,int Fa,int key)
{
    x=++tot;
    fa[x]=Fa;
    c[x][0]=c[x][1]=0;
    val[x]=key;
    siz[x]=cnt[x]=1;
}
void Rotate(int x,bool flag)
{
    int y=fa[x];
    c[y][!flag]=c[x][flag];
    fa[c[x][flag]]=y;
    if(fa[y]){
        c[fa[y]][c[fa[y]][1]==y]=x;
    }
    fa[x]=fa[y];
    c[x][flag]=y;
    fa[y]=x;
    Maintain(y);
}
void Splay(int &root,int x,int goal)
{
    if(!x){
        return;
    }
    int y;
    while((y=fa[x])!=goal){
        if(fa[y]==goal){
            Rotate(x,c[y][0]==x);
        }
        else{
            if((c[y][0]==x)==(c[fa[y]][0]==y)){
                Rotate(y,c[fa[y]][0]==y);
            }
            else{
                Rotate(x,c[y][0]==x);
                y=fa[x];
            }
            Rotate(x,c[y][0]==x);
        }
    }
    Maintain(x);
    if(!goal){
        root=x;
    }
}
int Find(int key,int x)
{
    while(c[x][val[x]<key]){
        if(val[x]==key){
            return x;
        }
        x=c[x][val[x]<key];
    }
    return x;
}
void Insert(int &root,int key)
{
    int x=Find(key,root);
    if(val[x]==key){
        ++cnt[x];
        Splay(root,x,0);
        return;
    }
    NewNode(c[x][val[x]<key],x,key);
    Splay(root,c[x][val[x]<key],0);
}

int roots[100005];
ll anss[100005];
bool vis[100005];
int e,first[100005],nex[200005],v[200005];
int dep[100005];
void AddEdge(int U,int V){
	v[++e]=V;
	nex[e]=first[U];
	first[U]=e;
}
int T,n,a[100005],b[100005];
bool cmp(const int &a,const int &b){
	return siz[roots[a]]>siz[roots[b]];
}
int sons[100005],BI;
void dfs(int U){
	for(int i=1;i<=cnt[U];++i){
		Insert(roots[sons[1]],val[U]);
		int X=Find(val[U],roots[sons[1]]);
		Splay(roots[sons[1]],X,0);
		if(c[X][0]){
			anss[BI]+=sum[c[X][0]];
		}
		anss[BI]+=(ll)(cnt[X]+(c[X][1] ? siz[c[X][1]] : 0))*(ll)val[U];
	}
	if(c[U][0]){
		dfs(c[U][0]);
	}
	if(c[U][1]){
		dfs(c[U][1]);
	}
}
int main(){
//	freopen("a.in","r",stdin);
//	freopen("a.out","w",stdout);
	scanf("%d",&T);
	int x,y;
	for(;T;--T){
		memset(first,0,sizeof(first));
		e=0;
		scanf("%d",&n);
		for(int i=1;i<=n;++i){
			scanf("%d",&a[i]);
		}
		for(int i=1;i<n;++i){
			scanf("%d%d",&x,&y);
			AddEdge(x,y);
			AddEdge(y,x);
		}
		memset(vis,0,sizeof(vis));
		memset(dep,0,sizeof(dep));
		memset(anss,0,sizeof(anss));
		queue<int>q;
		q.push(1);
		int t=0;
		while(!q.empty()){
			int U=q.front(); q.pop(); vis[U]=1;
			b[++t]=U;
			for(int i=first[U];i;i=nex[i]){
				if(!vis[v[i]]){
					dep[v[i]]=dep[U]+1;
					q.push(v[i]);
				}
			}
		}
		for(int i=t;i>=1;--i){
			BI=b[i];
			int dir_son=0;
			for(int j=first[b[i]];j;j=nex[j]){
				if(dep[v[j]]>dep[b[i]]){
					sons[++dir_son]=v[j];
				}
			}
			if(!dir_son){
				NewNode(roots[b[i]],0,a[b[i]]);
				anss[b[i]]=a[b[i]];
				continue;
			}
			sort(sons+1,sons+dir_son+1,cmp);
			anss[b[i]]=anss[sons[1]];
			for(int j=2;j<=dir_son;++j){
				dfs(roots[sons[j]]);
			}
			Insert(roots[sons[1]],a[b[i]]);
			int X=Find(a[b[i]],roots[sons[1]]);
			Splay(roots[sons[1]],X,0);
			if(c[X][0]){
				anss[BI]+=sum[c[X][0]];
			}
			anss[BI]+=(ll)(cnt[X]+(c[X][1] ? siz[c[X][1]] : 0))*(ll)a[b[i]];
			roots[b[i]]=roots[sons[1]];
		}
		for(int i=1;i<=n;++i){
			printf("%lld ",anss[i]);
		}
		puts("");
		memset(sum,0,sizeof(ll)*(tot+1));
		memset(fa,0,sizeof(int)*(tot+1));
		memset(val,0,sizeof(int)*(tot+1));
		for(int i=0;i<=tot;++i){
			c[i][0]=c[i][1]=0;
		}
		memset(siz,0,sizeof(int)*(tot+1));
		memset(cnt,0,sizeof(int)*(tot+1));
		memset(roots,0,sizeof(int)*(tot+1));
//		printf("%d\n",tot);
		tot=0;
	}
	return 0;
}
时间: 2024-08-24 02:57:30

【Splay】【启发式合并】hdu6133 Army Formations的相关文章

【BZOJ2733】永无乡[splay启发式合并or线段树合并]

题目大意:给你一些点,修改是在在两个点之间连一条无向边,查询时求某个点能走到的点中重要度第k大的点.题目中给定的是每个节点的排名,所以实际上是求第k小:题目求的是编号,不是重要度的排名.我一开始差点被这坑了. 网址:http://www.lydsy.com/JudgeOnline/problem.php?id=2733 这道题似乎挺经典的(至少我看许多神犇很早就做了这道题).这道题有两种写法:并查集+(splay启发式合并or线段树合并).我写的是线段树合并,因为--splay不会打+懒得学.

BZOJ2733 永无乡【splay启发式合并】

本文版权归ljh2000和博客园共有,欢迎转载,但须保留此声明,并给出原文链接,谢谢合作. 本文作者:ljh2000 作者博客:http://www.cnblogs.com/ljh2000-jump/转载请注明出处,侵权必究,保留最终解释权! Description 永无乡包含 n 座岛,编号从 1 到 n,每座岛都有自己的独一无二的重要度,按照重要度可 以将这 n 座岛排名,名次用 1 到 n 来表示.某些岛之间由巨大的桥连接,通过桥可以从一个岛 到达另一个岛.如果从岛 a 出发经过若干座(含

bzoj2733: [HNOI2012]永无乡(splay+启发式合并/线段树合并)

这题之前写过线段树合并,今天复习Splay的时候想起这题,打算写一次Splay+启发式合并. 好爽!!! 写了长长的代码(其实也不长),只凭着下午的一点记忆(没背板子...),调了好久好久,过了样例,submit,1A! 哇真的舒服 调试输出懒得删了QwQ #include<iostream> #include<cstdlib> #include<cstring> #include<cstdio> #include<queue> #include

【BZOJ-2809】dispatching派遣 Splay + 启发式合并

2809: [Apio2012]dispatching Time Limit: 10 Sec  Memory Limit: 128 MBSubmit: 2334  Solved: 1192[Submit][Status][Discuss] Description 在一个忍者的帮派里,一些忍者们被选中派遣给顾客,然后依据自己的工作获取报偿.在这个帮派里,有一名忍者被称之为 Master.除了 Master以外,每名忍者都有且仅有一个上级.为保密,同时增强忍者们的领导力,所有与他们工作相关的指令总是

Bzoj 2733: [HNOI2012]永无乡 数组Splay+启发式合并

2733: [HNOI2012]永无乡 Time Limit: 10 Sec  Memory Limit: 128 MBSubmit: 3955  Solved: 2112[Submit][Status][Discuss] Description 永无乡包含 n 座岛,编号从 1 到 n,每座岛都有自己的独一无二的重要度,按照重要度可 以将这 n 座岛排名,名次用 1 到 n 来表示.某些岛之间由巨大的桥连接,通过桥可以从一个岛 到达另一个岛.如果从岛 a 出发经过若干座(含 0 座)桥可以到达

【BZOJ2809】【splay启发式合并】dispatching

Description 在一个忍者的帮派里,一些忍者们被选中派遣给顾客,然后依据自己的工作获取报偿.在这个帮派里,有一名忍者被称之为 Master.除了 Master以外,每名忍者都有且仅有一个上级.为保密,同时增强忍者们的领导力,所有与他们工作相关的指令总是由上级发送给他的直接下属,而不允许通过其他的方式发送.现在你要招募一批忍者,并把它们派遣给顾客.你需要为每个被派遣的忍者 支付一定的薪水,同时使得支付的薪水总额不超过你的预算.另外,为了发送指令,你需要选择一名忍者作为管理者,要求这个管理者

[BZOJ2733] [HNOI2012] 永无乡 (splay启发式合并)

Description 永无乡包含 n 座岛,编号从 1 到 n,每座岛都有自己的独一无二的重要度,按照重要度可 以将这 n 座岛排名,名次用 1 到 n 来表示.某些岛之间由巨大的桥连接,通过桥可以从一个岛 到达另一个岛.如果从岛 a 出发经过若干座(含 0 座)桥可以到达岛 b,则称岛 a 和岛 b 是连 通的.现在有两种操作:B x y 表示在岛 x 与岛 y 之间修建一座新桥.Q x k 表示询问当前与岛 x连通的所有岛中第 k 重要的是哪座岛,即所有与岛 x 连通的岛中重要度排名第 k

bzoj 2733 Splay 启发式合并,名次树

题意:给定一个带点权的无向图,有两种操作: 1.将两个连通分量合并. 2.查询某个连通分量里的第K大点. 题解: 用并查集维护连通关系,一开始建立n棵splay树,然后不断合并,查询. 处理技巧: 1.每个顶点u所在的Splay就是T[find(u)]. 2.每个顶点在树中对应的节点编号就是该顶点的编号. 1 #include <cstdio> 2 #include <iostream> 3 #define maxn 100110 4 using namespace std; 5

splay启发式合并

3545: [ONTAK2010]Peaks Time Limit: 10 Sec  Memory Limit: 128 MBSubmit: 1889  Solved: 501[Submit][Status][Discuss] Description 在Bytemountains有N座山峰,每座山峰有他的高度h_i.有些山峰之间有双向道路相连,共M条路径,每条路径有一个困难值,这个值越大表示越难走,现在有Q组询问,每组询问询问从点v开始只经过困难值小于等于x的路径所能到达的山峰中第k高的山峰,如