[BZOJ1146]CTSC2008网络管理|树上带修改K大

  树上带修改K大,太可怕。。写了树链剖分+线段树套平衡树+二分和dfs序+主席树两种,每种都是写+调试花了将近5个小时!!我实在是太弱了。。

1.   树链剖分+线段树套平衡树+二分

最显然的做法了,没啥好多说的,不过写起来真是麻烦(我太弱),

一不小心就会把线段树和平衡树的节点的域弄混,犯了超级多傻逼错误。。写这题的时候还把自己树链剖分的风格改了一下,以前的实在是太麻烦了。。查询的时候二分答案,统计比当前的k大的数有多少个就行了。。

2.   dfs序+主席树

 考虑不带修改,那么可以对每个节点维护一颗权值线段树记录它到root的数,类型区间K大的,每个点的新树可以由父节点更新log n个节点来得到,所以初始化是O(Nlog
N),查询只要把这两个点和lca的权值权值线段树拉出来,二分答案像上面一样搞就行。。如果这两个点是x,y,lca是p,Ui表示i节点的权值线段树,答案就是Ux+Uy-2*Up再加上p的权值。。

 再来想带修改,大思路还是要把树转化成线性序列来搞。。那么就想到dfs序,若修改一个节点的权值,只会改变它的子树上的各点的权值线段树,而这些点再dfs序中又对应的是一段区间,那就有办法了。。用树状数组来维护所有的修改,若点i从a被修改为b,L[i]表示i在dfs序中出现的位置,R[i]表示i的子树上的节点在dfs中出现的最后位置,那么先将L[i]-n的a减去1,b加上1,再讲R[i]+1-n的a加上1,b减去1,那么就完成的对子树区间的修改。。具体查询的时候只要把dfs序当做区间k大那样做就行了,记得要加上原来未修改时的权值线段树。。

树剖+线段树套平衡树+二分:

#include<cstdio>
#include<iostream>
#define N 80005
#define NN 1600020
#define ls c[x][0]
#define rs c[x][1]
using namespace std;
struct edge{
	int e,next;
}ed[N*2];
int n,Q,s,e,ne=0,i,nd=0,cnt=0,rt,k,x,y,maxq=0,a[N],q[N],q1[N],fa[N],sz[N],top[N],son[N],d[N],xu[N],root[N*4],l[N*4],r[N*4],lc[N*4],rc[N*4],c[NN][2],size[NN],num[NN],same[NN],pre[NN];
void add(int s,int e)
{
	ed[++ne].e=e;
	ed[ne].next=a[s];a[s]=ne;
}
//处理size 选出son
void dfs1(int x)
{
	int to,ms=0;
	sz[x]=1;son[x]=0;
	for (int j=a[x];j;j=ed[j].next)
		if (ed[j].e!=fa[x])
		{
			to=ed[j].e;d[to]=d[x]+1;
			fa[to]=x;dfs1(to);
			sz[x]+=sz[to];
			if (ms<sz[to]) ms=sz[to],son[x]=to;
		}
}
//链剖
void dfs2(int x,int tp)
{
	xu[x]=++cnt;q1[cnt]=q[x];top[x]=tp;
	if (son[x]) dfs2(son[x],tp);
	for (int j=a[x];j;j=ed[j].next) if (ed[j].e!=fa[x]&&ed[j].e!=son[x]) dfs2(ed[j].e,ed[j].e);
}
//splay
void print(int x)
{
	if (!x) return;
	print(ls);printf("%d^%d ",num[x],same[x]);print(rs);
}
void newnode(int &x,int fa,int k)
{
	x=++nd;pre[x]=fa;
	num[x]=k;size[x]=same[x]=1;
	ls=rs=0;
}
void update(int x){size[x]=size[ls]+size[rs]+same[x];}
void rot(int x,int kind)
{
	int y=pre[x],z=pre[y];
	pre[c[x][kind]]=y;c[y][!kind]=c[x][kind];
	pre[y]=x;c[x][kind]=y;
	pre[x]=z;if (z) c[z][c[z][1]==y]=x;
	update(y);update(x);
}
void splay(int x,int rt)
{
	int y,z,kind;
	while (pre[x])
	{
		int y=pre[x];
		if (!pre[y]) rot(x,c[y][0]==x);
		else
		{
			z=pre[y];kind=(c[z][0]==y);
			if (c[y][kind]==x) rot(x,!kind);else rot(y,kind);
			rot(x,kind);
		}
	}
	root[rt]=x;
}
void ins(int &x,int k,int rt,int fa)
{
	if (!x) {newnode(x,fa,k);splay(x,rt);return;}
	if (num[x]==k){size[x]++;same[x]++;splay(x,rt);return;}
	ins(c[x][num[x]<k],k,rt,x);update(x);
}
void merge(int c1,int c2,int rt)
{
	pre[c1]=0;
	while (c[c1][1]) c1=c[c1][1];
	splay(c1,rt);c[c1][1]=c2;pre[c2]=c1;
	update(c1);
	root[rt]=c1;
}
int find(int x,int k){return num[x]==k?x:find(c[x][num[x]<k],k);}
void del(int x,int k,int rt)
{
	x=find(x,k);
	splay(x,rt);
	if (same[x]>1) {same[x]--;size[x]--;return;}
	if (ls*rs==0) {root[rt]=ls+rs,pre[ls+rs]=0;return;}
	merge(ls,rs,rt);
}
//初始化树套树
void build(int &x,int ll,int rr)
{
	x=++cnt;l[x]=ll;r[x]=rr;root[x]=0;
	ins(root[x],q1[ll],x,0);
	if (ll==rr)
	{
		lc[x]=rc[x]=0;
		return;
	}
	int mid=(ll+rr)>>1;
	for (int i=ll+1;i<=rr;i++) ins(root[x],q1[i],x,0);
	build(lc[x],ll,mid);build(rc[x],mid+1,rr);
}
void init()
{
	fa[1]=0;d[1]=0;size[0]=0;
	dfs1(1);dfs2(1,1);
	cnt=0;build(rt,1,n);
}
//修改&&询问
void change(int x,int ll,int k)
{
	if (!x) return;
	del(root[x],q1[ll],x);ins(root[x],k,x,0);//print(root[x]);printf("\n");printf("%d %d %d %d**\n",root[x],l[x],r[x],size[0]);
	if (ll>(l[x]+r[x])/2) change(rc[x],ll,k);else change(lc[x],ll,k);
}
int lca(int x,int y)
{
	if (d[top[x]]>d[top[y]]) swap(x,y);
	while (top[x]!=top[y])
	{
		y=fa[top[y]];
		if (d[top[x]]>d[top[y]]) swap(x,y);
	}
	return d[x]<d[y]?x:y;
}
int getsz(int x,int k)
{
	int ans=0;
	while (x)
	{//printf("here:%d %d %d %d\n",k,num[x],size[rs],same[x]);
		if (num[x]>k) ans+=size[rs]+same[x];
		else if (num[x]==k) return ans+size[rs];
		x=c[x][num[x]<k];
	}
	return ans;
}
int query(int x,int ll,int rr,int k)//查询比k大的数量
{
	if (ll>r[x]||rr<l[x]) return 0;
	if (ll<=l[x]&&rr>=r[x])
	{
	//	print(root[x]);printf("*\n");
		return getsz(root[x],k);
	}
	return query(lc[x],ll,rr,k)+query(rc[x],ll,rr,k);
}
int sum(int x,int y,int k)
{
	int ans=0;
	while (top[x]!=top[y])
	{
		if (d[top[x]]>d[top[y]]) swap(x,y);
		ans+=query(rt,xu[top[y]],xu[y],k);//printf("$%d %d %d\n",xu[top[y]],xu[y],ans);
		y=fa[top[y]];
	}
	ans+=query(rt,min(xu[x],xu[y]),max(xu[x],xu[y]),k);
	return ans;
}
void solve(int x,int y,int k)
{
	int p=lca(x,y),l=1,r=maxq,mid,t;
	if (d[x]+d[y]-2*d[p]+1<k) {printf("invalid request!\n");return;}
	while (l<r)
	{
		mid=(l+r)>>1;
		t=sum(x,y,mid);//printf("re:%d %d\n",mid,t);
		if (t>=k) l=mid+1;else r=mid;
	}
	printf("%d\n",l);
}
int main()
{
	freopen("1146.in","r",stdin);
	scanf("%d%d",&n,&Q);
	for (i=1;i<=n;i++) scanf("%d",&q[i]),a[i]=0,maxq=max(maxq,q[i]);
	for (i=1;i<n;i++)
	{
		scanf("%d%d",&s,&e);
		add(s,e);add(e,s);
	}
	init();
//	for (i=1;i<=n;i++) printf("%d ",q1[i]);printf("\n");
	while (Q--)
	{
		scanf("%d%d%d",&k,&x,&y);
		if (!k) change(rt,xu[x],y),q1[xu[x]]=y,maxq=max(maxq,y);else solve(x,y,k);//printf("%d cao\n",size[0]);
	}
}

dfs序+主席树:

#include<iostream>
#include<cstdio>
#include<algorithm>
#define N 80010
#define NN 8000010
using namespace std;
struct edge{
	int e,xu,next;
}ed[N*4];//兼并了图的边和询问lca点对
struct node{
	int n,yuan;
	node(){}
	node(int n,int yuan):n(n),yuan(yuan){}
}num[2*N];
int n,Q,i,ne=0,nd=0,tim=0,cnt=0,tot=0,s,e,sum[2],d[N],a[N],L[N],R[N],fa[N],pre[N],size[NN],lc[NN],rc[NN],lca[N],k[N],x[N],y[N],u[N],h[N],q[N],newq[2*N],root[N],c[N],use[N][2];
int lowbit(int x){return x&-x;}
int getfa(int x){return fa[x]==x?x:fa[x]=getfa(fa[x]);}
void add(int s,int e)
{
	ed[++ne].e=e;ed[ne].next=a[s];a[s]=ne;
}
void add2(int s,int e,int xu)
{
	ed[++ne].e=e;ed[ne].xu=xu;ed[ne].next=h[s];h[s]=ne;
}
bool cmp(node a,node b) {return a.n<b.n;}
int update(int l,int r,int last,int k,int del)
{
	int x=++nd,mid=(l+r)>>1;
	size[x]=size[last]+del;
	lc[x]=rc[x]=0;
	if (l==r) return x;
	if (k<=mid) lc[x]=update(l,mid,lc[last],k,del),rc[x]=rc[last];else rc[x]=update(mid+1,r,rc[last],k,del),lc[x]=lc[last];
	return x;
}
void dfs(int x)
{
	fa[x]=x;L[x]=++tim;u[x]=0;
	root[x]=update(1,tot,root[pre[x]],q[x],1);
	for (int j=a[x];j;j=ed[j].next)
		if (ed[j].e!=pre[x])
		{
			d[ed[j].e]=d[x]+1;pre[ed[j].e]=x;
			dfs(ed[j].e);fa[ed[j].e]=x;
		}
	R[x]=tim;u[x]=1;
	for (int j=h[x];j;j=ed[j].next)
		if (u[ed[j].e]) lca[ed[j].xu]=getfa(ed[j].e);
}
void change(int x,int k,int del)
{
	for (int i=x;i<=n;i+=lowbit(i)) c[i]=update(1,tot,c[i],k,del);
}
void get(int x,int kind)
{
	for (int i=x;i;i-=lowbit(i)) use[++sum[kind]][kind]=c[i];
}
int calc(int kind)
{
	int ans=0;
	for (int i=1;i<=sum[kind];i++) ans+=size[rc[use[i][kind]]];
	return ans;
}
void next(int kind,int dir)
{
	for (int i=1;i<=sum[kind];i++) use[i][kind]=dir?rc[use[i][kind]]:lc[use[i][kind]];
}
void query(int p,int x,int y,int k)
{
	if (d[x]+d[y]-2*d[p]+1<k) {printf("invalid request!\n");return;}
	sum[0]=sum[1]=0;
	get(L[x],1);get(L[y],1);get(L[p],0);
	int ux=root[x],uy=root[y],up=root[p];
	int l=1,r=tot,mid,now;
	bool f=true;
	while (l<r)
	{
		mid=(l+r)>>1;
		now=size[rc[ux]]+calc(1)+size[rc[uy]]-2*(calc(0)+size[rc[up]]);
		if (f)now+=(q[p]>mid);
		if (now>=k)
		{
			next(1,1);next(0,1);ux=rc[ux];uy=rc[uy];up=rc[up];
			l=mid+1;
		}
		else
		{
			next(1,0);next(0,0);ux=lc[ux];uy=lc[uy];up=lc[up];
			if (q[p]>mid) f=false;
			r=mid,k-=now;
		}
	}
	printf("%d\n",newq[l]);
}
int main()
{
	scanf("%d%d",&n,&Q);
	for (i=1;i<=n;i++)
	{
		scanf("%d",&q[i]);
		num[++cnt]=node(q[i],i);
		a[i]=h[i]=c[i]=0;
	}
	for (i=1;i<n;i++)
	{
		scanf("%d%d",&s,&e);
		add(s,e);add(e,s);
	}
	for (i=1;i<=Q;i++)
	{
		scanf("%d%d%d",&k[i],&x[i],&y[i]);
		if (!k[i])num[++cnt]=node(y[i],n+i);else add2(x[i],y[i],i),add2(y[i],x[i],i);
	}
	sort(num+1,num+1+cnt,cmp);
	for (i=1;i<=cnt;i++)
	{
		if (num[i].n!=num[i-1].n) newq[++tot]=num[i].n;
		if (num[i].yuan>n) y[num[i].yuan-n]=tot;else q[num[i].yuan]=tot;
	}
	d[(n+1)/2]=0;pre[(n+1)/2]=0;root[0]=0;lc[0]=rc[0]=0;size[0]=0;
	dfs((n+1)/2);
	for (i=1;i<=Q;i++)
	if (k[i]) query(lca[i],x[i],y[i],k[i]);
	else
	{
		change(L[x[i]],q[x[i]],-1);change(R[x[i]]+1,q[x[i]],1);
		change(L[x[i]],y[i],1);change(R[x[i]]+1,y[i],-1);
		q[x[i]]=y[i];
	}
}
时间: 2024-10-12 17:31:15

[BZOJ1146]CTSC2008网络管理|树上带修改K大的相关文章

[BZOJ 1146]网络管理Network 树上带修改路径k值

题目意思非常清楚,就是要求树上带修改的路径k大值 如果不带修改的话,我会用树上主席树去搞,以父子关系建树,可以参见 [BZOJ 3123]森林 但是带修改就不会打了QAQ,于是去学了另一种在dfs序上搞的方法(同时感谢呵呵酵母菌的帮助) 其实思想是一样的,就是搞出来节点到根路径的线段树,然后用x+y-lca-fa(lca)去差分出来树上路径的线段树,再去里面查询k值 那么我们怎么得到点到根路径的线段树呢?可以在dfs序上差分啊! dfs序列上的dfnl[x]~dfnr[x]包含的是以x为根节点的

[BZOJ 4129]Haruna’s Breakfast(树上带修改莫队)

Description Haruna每天都会给提督做早餐! 这天她发现早饭的食材被调皮的 Shimakaze放到了一棵 树上,每个结点都有一样食材,Shimakaze要考验一下她. 每个食材都有一个美味度,Shimakaze会进行两种操作: 1.修改某个结点的食材的美味度. 2.对于某条链,询问这条链的美味度集合中,最小的未出现的自然数是多少.即mex值. 请你帮帮Haruna吧. Solution 树上带修改莫队 统计答案的时候也分块查询,找到第一个没满的块开始一个一个找 #include<i

[UOJ #58][WC2013]糖果公园(树上带修改莫队)

Description Solution 树上带修改莫队…!VFK的题解写得很清楚啦 (我的程序为什么跑得这么慢…交的时候总有一种自己在卡测评的感觉…) #include<iostream> #include<cstdio> #include<cstdlib> #include<cstring> #include<cmath> #include<algorithm> #define MAXN 100005 typedef long l

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条高速光缆组成. 每个部门都有一个专属的路由器,

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

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

bzoj1146 [CTSC2008]网络管理Network

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

【Luogu P4074】[WC2013]糖果公园(树上带修改莫队)

题目描述 Candyland 有一座糖果公园,公园里不仅有美丽的风景.好玩的游乐项目,还有许多免费糖果的发放点,这引来了许多贪吃的小朋友来糖果公园游玩. 糖果公园的结构十分奇特,它由 \(n\) 个游览点构成,每个游览点都有一个糖果发放处,我们可以依次将游览点编号为 \(1\) 至 \(n\).有 \(n-1\) 条双向道路连接着这些游览点,并且整个糖果公园都是连通的,即从任何一个游览点出发都可以通过这些道路到达公园里的所有其它游览点. 糖果公园所发放的糖果种类非常丰富,总共有 \(m\) 种,

【树链剖分】【函数式权值分块】bzoj1146 [CTSC2008]网络管理Network

裸题,直接上.复杂度O(n*sqrt(n)*log(n)). //Num[i]表示树中的点i在函数式权值分块中对应的点 //Map[i]表示函数式权值分块中的点i在树中对应的点 #include<cstdio> #include<algorithm> #include<cmath> using namespace std; #define N 80001 #define INF 2147483647 #define NN 87001 #define BN 296 int

[TS-A1505] [清橙2013中国国家集训队第二次作业] 树 [可持久化线段树,求树上路径第k大]

按Dfs序逐个插入点,建立可持久化线段树,每次查询即可,具体详见代码. 不知道为什么,代码慢的要死,, #include <iostream> #include <algorithm> #include <cstdio> #include <cstdlib> #include <cstring> #include <cmath> #include <ctime> #include <vector> using