BZOJ 1146 CTSC 2008 网络管理 Network 树链剖分+二分答案+平衡树

题目大意:有n个路由器,他们由n-1条边连接(形成一棵树)。每一个路由器有一个延时值。有两种操作:

1.查询树上x,y两点之间的路径上第k大的权值是多少

2.修改x位置的权值为y

思路:当我大概想到怎么做这个题的时候,所想的时间复杂度已经达到了O(nlog^4n),偷偷的瞄了一眼数据范围...(N,Q<=80000,时限50s,小心翼翼的掏出计算器算了一下:8w * log(8w) ^  4 ≈ 56E,心中这样想着:Treap有常数,链剖常数大,二分不稳定的范围好像不止8w...评测机会不会很卡...代码一定很长吧...写出来不就T了么...哎...这个世界啊..然后默默的想关掉页面。这时候,一位学长站在我的后面,我向他倾诉了我的苦衷。他听了之后居然说:

“没事,写吧,我10多秒就过了。。。”

秒就过了。。。就过了。。。过了。。。了。。。。。。

还是正经点说说思路吧。树上路径,lca什么的要不倍增,要不树链剖分,带修改,只能用树链剖分。第k大,就是二分+平衡树,二分第k大是谁,然后压缩上下界。修改的时候要在每个包含这个节点的线段树中的线段中删除这个节点,然后加上要改成的点。

CODE:

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define MAX 80010
#define LEFT (pos << 1)
#define RIGHT (pos << 1|1)
#define SIZE(a) (a == NULL ? 0:a->size)
using namespace std;

struct Complex{
	int val,random,size,cnt;
	Complex *son[2],*father;

	Complex(int _) {
		val = _;
		random = rand();
		size = cnt = 1;
		son[0] = son[1] = NULL;
	}
	int Compare(int x) {
		if(x == val)	return -1;
		return x > val;
	}
	void Maintain() {
		size = cnt;
		if(son[0] != NULL)	size += son[0]->size;
		if(son[1] != NULL)	size += son[1]->size;
	}
}*root;

int points,asks;
int src[MAX];
int head[MAX],total;
int next[MAX << 1],aim[MAX << 1];

int deep[MAX],son[MAX],father[MAX];
int top[MAX],pos[MAX],temp[MAX],cnt;

Complex *tree[MAX << 2];

inline void Add(int x,int y);
int PreDFS(int x,int last);
void DFS(int x,int last,int _top);

void BuildTree(int l,int r,int pos);
void Modify(int l,int r,int x,int pos,int _,int __);
int Bisection(int x,int y,int k);
int Ask(int x,int y,int k);
int Ask(int l,int r,int x,int y,int pos,int k);

inline void Rotate(Complex *&a,bool dir);
void Insert(Complex *&a,int x);
void Delete(Complex *&a,int x);
int GetRank(Complex *a,int k);

int main()
{
	cin >> points >> asks;
	for(int i = 1;i <= points; ++i)
		scanf("%d",&src[i]);
	for(int x,y,i = 1;i < points; ++i) {
		scanf("%d%d",&x,&y);
		Add(x,y),Add(y,x);
	}
	PreDFS(1,0);
	DFS(1,0,1);
	BuildTree(1,cnt,1);
	for(int flag,x,y,i = 1;i <= asks; ++i) {
		scanf("%d%d%d",&flag,&x,&y);
		if(!flag) {
			Modify(1,cnt,pos[x],1,y,src[x]);
			src[x] = y;
		}
		else {
			int temp = Bisection(x,y,flag) - 1;
			if(temp == -1)	puts("invalid request!");
			else	printf("%d\n",temp);
		}
	}
	return 0;
}

inline void Add(int x,int y)
{
	next[++total] = head[x];
	aim[total] = y;
	head[x] = total;
}

int PreDFS(int x,int last)
{
	father[x] = last;
	deep[x] = deep[last] + 1;
	int max_size = 0,re = 1;
	for(int i = head[x];i;i = next[i]) {
		if(aim[i] == last)	continue;
		int temp = PreDFS(aim[i],x);
		re += temp;
		if(temp > max_size)
			son[x] = aim[i],max_size = temp;
	}
	return re;
}

void DFS(int x,int last,int _top)
{
	temp[++cnt] = src[x];
	pos[x] = cnt;
	top[x] = _top;
	if(son[x])	DFS(son[x],x,_top);
	for(int i = head[x];i;i = next[i]) {
		if(aim[i] == last || aim[i] == son[x])	continue;
		DFS(aim[i],x,aim[i]);
	}
}

void BuildTree(int l,int r,int pos)
{
	for(int i = l;i <= r; ++i)
		Insert(tree[pos],temp[i]);
	if(l == r)	return ;
	int mid = (l + r) >> 1;
	BuildTree(l,mid,LEFT);
	BuildTree(mid + 1,r,RIGHT);
}

void Modify(int l,int r,int x,int pos,int _,int __)
{
	Delete(tree[pos],__);
	Insert(tree[pos],_);
	if(l == r)	return ;
	int mid = (l + r) >> 1;
	if(x <= mid)	Modify(l,mid,x,LEFT,_,__);
	else	Modify(mid + 1,r,x,RIGHT,_,__);
}

int Bisection(int x,int y,int k)
{
	int l = 0,r = static_cast<int>(1e8),ans = 0;
	while(l <= r) {
		int mid = (l + r) >> 1;
		if(Ask(x,y,mid) >= k)
			l = mid + 1;
		else	r = mid - 1,ans = mid;
	}
	return ans;
}

int Ask(int x,int y,int k)
{
	int re = 0;
	while(top[x] != top[y]) {
		if(deep[top[x]] < deep[top[y]])
			swap(x,y);
		re += Ask(1,cnt,pos[top[x]],pos[x],1,k);
	x = father[top[x]];
	}
	if(deep[x] > deep[y])	swap(x,y);
	re += Ask(1,cnt,pos[x],pos[y],1,k);
	return re;
}

int Ask(int l,int r,int x,int y,int pos,int k)
{
	if(l == x && r == y)	return GetRank(tree[pos],k);
	int mid = (l + r) >> 1;
	if(y <= mid)	return Ask(l,mid,x,y,LEFT,k);
	else if(x > mid)	return Ask(mid + 1,r,x,y,RIGHT,k);
	int left = Ask(l,mid,x,mid,LEFT,k);
	int right = Ask(mid + 1,r,mid + 1,y,RIGHT,k);
	return left + right;
}

inline void Rotate(Complex *&a,bool dir)
{
	Complex *k = a->son[!dir];
	a->son[!dir] = k->son[dir];
	k->son[dir] = a;
	a->Maintain(),k->Maintain();
	a = k;
}

void Insert(Complex *&a,int x)
{
	if(a == NULL) {
		a = new Complex(x);
		return ;
	}
	int dir = a->Compare(x);
	if(dir == -1)
		a->cnt++;
	else {
		Insert(a->son[dir],x);
		if(a->son[dir]->random > a->random)
			Rotate(a,!dir);
	}
	a->Maintain();
}

void Delete(Complex *&a,int x)
{
	int dir = a->Compare(x);
	if(dir != -1)
		Delete(a->son[dir],x);
	else {
		if(a->cnt > 1)
			--a->cnt;
		else {
			if(a->son[0] == NULL)	a = a->son[1];
			else if(a->son[1] == NULL)	a = a->son[0];
			else {
				bool _ = (a->son[0]->random > a->son[1]->random);
				Rotate(a,_);
				Delete(a->son[_],x);
			}
		}
	}
	if(a != NULL)	a->Maintain();
}

int GetRank(Complex *a,int k)
{
	if(a == NULL)	return 0;
	if(k > a->val)	return GetRank(a->son[1],k);
	if(k == a->val)	return a->cnt + GetRank(a->son[1],k);
	return SIZE(a->son[1]) + a->cnt + GetRank(a->son[0],k);
}
时间: 2024-08-12 10:55:15

BZOJ 1146 CTSC 2008 网络管理 Network 树链剖分+二分答案+平衡树的相关文章

BZOJ 1146: [CTSC2008]网络管理Network( 树链剖分 + 树状数组套主席树 )

树链剖分完就成了一道主席树裸题了, 每次树链剖分找出相应区间然后用BIT+(可持久化)权值线段树就可以完成计数. 但是空间问题很严重....在修改时不必要的就不要新建, 直接修改原来的..详见代码. 时间复杂度O(N*log^3(N)) ---------------------------------------------------------------------------- #include<cstdio> #include<cstring> #include<

BZOJ1146 [CTSC2008]网络管理Network 树链剖分 主席树 树状数组

欢迎访问~原文出处--博客园-zhouzhendong 去博客园看该题解 题目传送门 - BZOJ1146 题意概括 在一棵树上,每一个点一个权值. 有两种操作: 1.单点修改 2.询问两点之间的树链上的第k大值 题解 水题. 就是烦了一点. 树链剖分+带修主席树. 带修主席树: BZOJ1901 Zju2112 Dynamic Rankings 主席树 代码 #include <cstring> #include <cstdio> #include <algorithm&g

hdu4729 树链剖分+二分

An Easy Problem for Elfness Time Limit: 5000/2500 MS (Java/Others)    Memory Limit: 65535/65535 K (Java/Others)Total Submission(s): 1235    Accepted Submission(s): 257 Problem Description Pfctgeorge is totally a tall rich and handsome guy. He plans t

【BZOJ 1146】 [CTSC2008]网络管理Network

1146: [CTSC2008]网络管理Network Time Limit: 50 Sec  Memory Limit: 162 MB Submit: 1938  Solved: 577 [Submit][Status] Description M公司是一个非常庞大的跨国公司,在许多国家都设有它的下属分支机构或部门.为了让分布在世界各地的N个部门之间协同工作,公司搭建了一个连接整个公司的通信网络.该网络的结构由N个路由器和N-1条高速光缆组成.每个部门都有一个专属的路由器,部门局域网内的所有机

BZOJ 1969: [Ahoi2005]LANE 航线规划( 树链剖分 )

首先我们要时光倒流, 倒着做, 变成加边操作维护关键边. 先随意搞出一颗树, 树上每条边都是关键边(因为是树, 去掉就不连通了)....然后加边(u, v)时, 路径(u, v)上的所有边都变成非关键边了, 因为形成了环, 环上任意2点有2条路径...下图, 加上蓝色的边, 红色x的边就变成了非关键边. 所以树链剖分维护一下...时间复杂度O(NlogN+MlogM+Qlog^2N), 可以AC. 翻了翻ZY的标解:“动态维护树+最近公共祖先查询”....复杂度是O(NlogN+M+QlogN)

Bzoj 4196: [Noi2015]软件包管理器 树链剖分

4196: [Noi2015]软件包管理器 Time Limit: 10 Sec  Memory Limit: 512 MBSubmit: 721  Solved: 419[Submit][Status][Discuss] Description Linux用户和OSX用户一定对软件包管理器不会陌生.通过软件包管理器,你可以通过一行命令安装某一个软件包,然后软件包管理器会帮助你从软件源下载软件包,同时自动解决所有的依赖(即下载安装这个软件包的安装所依赖的其它软件包),完成所有的配置.Debian

NOIP 2015 BZOJ 4326 运输计划 (树链剖分+二分)

题目大意:给一棵带有边权的树,有m条路径,可以使一条边的权值为0,求最远路径的最小值.题解:因为题目的数据点给的很明确 因此可以打n*n的去骗前五十分.另外m=1时可以特判另外打个程序骗60分.60分程序: #include<cstdio> #include<algorithm> int dep[100001],fa[100001],last[200001],next[200001],e[200001],val[200001],cost[100001],tot,a,b,vv,u[1

BZOJ 4326 树链剖分+二分+差分+记忆化

去年NOIP的时候我还不会树链剖分! 还是被UOJ 的数据卡了一组. 差分的思想还是很神啊! 1 #include <iostream> 2 #include <cstring> 3 #include <cstdio> 4 #include <algorithm> 5 #include <ctime> 6 #include <cstdlib> 7 using namespace std; 8 const int Maxn=300100

cogs 2652. 秘术「天文密葬法」(0/1分数规划 长链剖分 二分答案 dp

http://cogs.pro:8080/cogs/problem/problem.php?pid=vSXNiVegV 题意:给个树,第i个点有两个权值ai和bi,现在求一条长度为m的路径,使得Σai/Σbi最小. 思路:二分答案得p,把每个点权值变成ai-p*bi,看是否存在长为一条长为m的路使总和<=0. tag数组表示从当前位置沿最长链走到底的值,dp数组初值表示从当前位置的重儿子走到底的值(加负号),用tag[...]+dp[..]维护从当前节点往下走若干步得到的最小值(只更新dp数组