【BZOJ】1146: [CTSC2008]网络管理Network(树链剖分+线段树套平衡树+二分 / dfs序+树状数组+主席树)

第一种做法(时间太感人):

这题我真的逗了,调了一下午,疯狂造数据,始终找不到错。

后来发现自己sb了,更新那里没有打id,直接套上u了。我。。。。

调了一下午啊!一下午的时光啊!本来说好中午A掉去学习第二种做法,噗

好吧,现在第一种做法是hld+seg+bst+二分,常数巨大,log^4级别,目前只会这种。

树剖后仍然用线段树维护dfs序区间,然后在每个区间建一颗平衡树,我用treap,(这题找最大啊,,,囧,并且要注意,这里的rank是比他大的数量,so,我们在二分时判断要判断一个范围,即要加上它重叠的数量,这点自己调试的时候找出来了)

我们将路径放进一个池子里,然后累计排名就行了。

时间很感人啊。

(希望以后不要犯这种sb错了,唉,太逗。)

#include <iostream>
#include <cstdio>
#include <cstdlib>
using namespace std;
#define dbg(x) cout << #x << "=" << x << endl
#define read(x) x=getint()
#define rdm(u) for(int i=ihead[u]; i; i=e[i].next)
#define lc x<<1
#define rc x<<1|1
#define lson l, m, lc
#define rson m+1, r, rc
#define MID (l+r)>>1
inline const int getint() { char c=getchar(); int ret=0, k=1; for(; c<‘0‘||c>‘9‘; c=getchar()) if(c==‘-‘) k=-1; for(; c>=‘0‘&&c<=‘9‘; c=getchar()) ret=ret*10+c-‘0‘; return k*ret; }
const int N=80010, oo=~0u>>1;
int n, q, ihead[N], brr[N], arr[N], cnt, bak, flg, L, R, same;
int fa[N], top[N], son[N], dep[N], sz[N], id[N], tot, num;
struct Ed { int to, next; }e[N<<1];
inline void add(const int &u, const int &v) {
	e[++cnt].next=ihead[u]; ihead[u]=cnt; e[cnt].to=v;
	e[++cnt].next=ihead[v]; ihead[v]=cnt; e[cnt].to=u;
}

struct node* null;
struct node {
	node* ch[2]; int wei, key, sz, cnt;
	void pushup() { sz=ch[0]->sz+ch[1]->sz+cnt; }
	node(int _w=0, int _sz=1, int _cnt=1) : key(_w), sz(_sz), cnt(_cnt) {
		ch[0]=ch[1]=null; wei=rand();
	}
}*root[N*50], *nd[N*50];
inline void rot(node* &x, const bool d) {
	node* t=x->ch[!d]; x->ch[!d]=t->ch[d]; t->ch[d]=x;
	x->pushup(); t->pushup();
	x=t;
}
void insert(node* &x, const int &key) {
	if(x==null) { x=new node(key); return; }
	if(key==x->key) { ++x->cnt; ++x->sz; return; }
	bool d=key>x->key;
	insert(x->ch[d], key);
	if(x->wei>x->ch[d]->wei) rot(x, !d);
	x->pushup();
}
void remove(node* &x, const int &key) {
	if(x==null) return;
	bool d=key>x->key;
	if(key==x->key) {
		if(x->cnt>1) { --x->cnt; --x->sz; return; }
		d=x->ch[0]->wei > x->ch[1]->wei;
		if(x->ch[d]==null) {
			delete x;
			x=null;
			return;
		}
		rot(x, !d);
		remove(x->ch[!d], key);
	}
	else remove(x->ch[d], key);
	x->pushup();
}
inline int rank(node* x, const int &key) {
	int ret=0, s;
	while(x!=null) {
		s=x->ch[1]->sz + x->cnt;
		if(key==x->key) same+=x->cnt;
		if(key<x->key) ret+=s, x=x->ch[0];
		else x=x->ch[1];
	}
	return ret;
}
void build(const int &l, const int &r, const int &x) {
	if(l==r) { root[x]=new node(brr[l]); return; }
	int m=MID;
	build(lson); build(rson);
	root[x]=new node(brr[l]);
	for(int i=l+1; i<=r; ++i) insert(root[x], brr[i]);
}
void update(const int &l, const int &r, const int &x) {
	if(l==r) {
		remove(root[x], bak);
		insert(root[x], flg);
		return;
	}
	int m=MID;
	remove(root[x], bak);
	insert(root[x], flg);
	if(L<=m) update(lson); if(m<R) update(rson);
}
void query(const int &l, const int &r, const int &x) {
	if(L<=l && r<=R) { nd[++num]=root[x]; return; }
	int m=MID;
	if(L<=m) query(lson);
	if(m<R) query(rson);
}
void dfs1(const int &u) {
	sz[u]=1; int v;
	rdm(u) if(fa[u]!=(v=e[i].to)) {
		fa[v]=u; dep[v]=dep[u]+1;
		dfs1(v);
		sz[u]+=sz[v];
		if(sz[v]>sz[son[u]]) son[u]=v;
	}
}
void dfs2(const int &u, const int &tp) {
	id[u]=++tot; top[u]=tp; brr[tot]=arr[u];
	if(son[u]) dfs2(son[u], tp);
	rdm(u) if(fa[u]!=e[i].to && son[u]!=e[i].to) dfs2(e[i].to, e[i].to);
}
void getrange(int x, int y) {
	num=0;
	int fx=top[x], fy=top[y];
	while(fx!=fy) {
		if(dep[fx]<dep[fy]) { swap(x, y); swap(fx, fy); }
		L=id[fx], R=id[x];
		query(1, n, 1);
		x=fa[fx]; fx=top[x];
	}
	if(dep[x]>dep[y]) swap(x, y);
	L=id[x], R=id[y];
	query(1, n, 1);
}
int getrank(const int &key) {
	int ret=0; same=0;
	for(int i=1; i<=num; ++i) ret+=rank(nd[i], key);
	return ret;
}
int getans(int x, int y, int k) {
	getrange(x, y);
	node* rt; int l=oo+1, r=oo, s=0;
	for(int i=1; i<=num; ++i) s+=nd[i]->sz;
	if(s<k) return l;
	for(int i=1; i<=num; ++i) {
		rt=nd[i];
		while(rt!=null) {
			if(rt->key<l) { rt=rt->ch[1]; continue; }
			if(rt->key>r) { rt=rt->ch[0]; continue; }
			s=getrank(rt->key);
			if(s+1<=k && k<=s+same)  return rt->key;  //这里要注意
			if(s+same>k) { l=rt->key; rt=rt->ch[1]; }
			else { r=rt->key; rt=rt->ch[0]; }
		}
	}
	return l;
}
int main() {
	null=new node(0, 0, 0); null->wei=oo;
	read(n); read(q);
	int u, v, k, ans;
	for(int i=1; i<=n; ++i) read(arr[i]);
	for(int i=1; i<n; ++i) { read(u); read(v); add(u, v); }
	dfs1(1); dfs2(1, 1); build(1, n, 1);
	while(q--) {
		read(k); read(u); read(v);
		if(!k) {
			bak=arr[u]; flg=arr[u]=v;
			L=R=id[u];
			update(1, n, 1);
		}
		else {
			ans=getans(u, v, k);
			if(ans==oo+1) puts("invalid request!");
			else printf("%d\n", ans);
		}
	}
	return 0;
}

【BZOJ】1146: [CTSC2008]网络管理Network(树链剖分+线段树套平衡树+二分 / dfs序+树状数组+主席树),布布扣,bubuko.com

时间: 2024-10-05 08:06:51

【BZOJ】1146: [CTSC2008]网络管理Network(树链剖分+线段树套平衡树+二分 / dfs序+树状数组+主席树)的相关文章

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

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

BZOJ 1146: [CTSC2008]网络管理Network [树上带修改主席树]

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

BZOJ 1146: [CTSC2008]网络管理Network

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

HDU 2460 Network(双连通+树链剖分+线段树)

HDU 2460 Network 题目链接 题意:给定一个无向图,问每次增加一条边,问个图中还剩多少桥 思路:先双连通缩点,然后形成一棵树,每次增加一条边,相当于询问这两点路径上有多少条边,这个用树链剖分+线段树处理 代码: #include <cstdio> #include <cstring> #include <algorithm> #include <vector> using namespace std; #pragma comment(linke

bzoj 2157: 旅游【树链剖分+线段树】

裸的树链剖分+线段树 但是要注意一个地方--我WA了好几次才发现取完相反数之后max值和min值是要交换的-- #include<iostream> #include<cstdio> using namespace std; const int N=200005; int n,m,h[N],cnt,de[N],va[N],fa[N],si[N],hs[N],fr[N],id[N],tot,rl[N]; char c[10]; struct qwe { int ne,no,to,va

【bzoj1146】[CTSC2008]网络管理Network 倍增LCA+dfs序+树状数组+主席树

题目描述 M公司是一个非常庞大的跨国公司,在许多国家都设有它的下属分支机构或部门.为了让分布在世界各地的N个部门之间协同工作,公司搭建了一个连接整个公司的通信网络.该网络的结构由N个路由器和N-1条高速光缆组成.每个部门都有一个专属的路由器,部门局域网内的所有机器都联向这个路由器,然后再通过这个通信子网与其他部门进行通信联络.该网络结构保证网络中的任意两个路由器之间都存在一条直接或间接路径以进行通信. 高速光缆的数据传输速度非常快,以至于利用光缆传输的延迟时间可以忽略.但是由于路由器老化,在这些

Aizu 2450 Do use segment tree 树链剖分+线段树

Do use segment tree Time Limit: 1 Sec Memory Limit: 256 MB 题目连接 http://www.bnuoj.com/v3/problem_show.php?pid=39566 Description Given a tree with n (1 ≤ n ≤ 200,000) nodes and a list of q (1 ≤ q ≤ 100,000) queries, process the queries in order and out

Hdu 3966 Aragorn&#39;s Story (树链剖分 + 线段树区间更新)

题目链接: Hdu 3966 Aragorn's Story 题目描述: 给出一个树,每个节点都有一个权值,有三种操作: 1:( I, i, j, x ) 从i到j的路径上经过的节点全部都加上x: 2:( D, i, j, x ) 从i到j的路径上经过的节点全部都减去x: 3:(Q, x) 查询节点x的权值为多少? 解题思路: 可以用树链剖分对节点进行hash,然后用线段树维护(修改,查询),数据范围比较大,要对线段树进行区间更新 1 #include <cstdio> 2 #include

【bzoj3589】动态树 树链剖分+线段树

题目描述 别忘了这是一棵动态树, 每时每刻都是动态的. 小明要求你在这棵树上维护两种事件 事件0:这棵树长出了一些果子, 即某个子树中的每个节点都会长出K个果子. 事件1:小明希望你求出几条树枝上的果子数. 一条树枝其实就是一个从某个节点到根的路径的一段. 每次小明会选定一些树枝, 让你求出在这些树枝上的节点的果子数的和. 注意, 树枝之间可能会重合, 这时重合的部分的节点的果子只要算一次. 输入 第一行一个整数n(1<=n<=200,000), 即节点数. 接下来n-1行, 每行两个数字u,