QAQ的LIS树 QAQ的LIS树2 题解报告

这两道题实际上考试的时候是一道题OwO

太可怕了,忙了我三个多小时,写了整整7K

这个题两个询问关联性不强,所以分开来考虑

QAQ的LIS树

考虑如何用dp求解答案

设dp(v)表示v到根的修改后的序列的和,c(v)是v点点权

那么v的答案就是dp(v)减去v到根的点权和

最直观的想法是我们从v点向上暴力跳父亲,跳到第一个不用修改的点u

不难发现因为u没有修改,所以之后的序列和u之后的序列是完全一样的

可以直接转移给v了,那么dp(v)的值就是dp(u)和v->u的序列和了

注意到v->u的所有点我们一定会修改,所以其序列形式一定是c(v),c(v)+1,c(v)+2……

这个数列的和直接等差数列求和即可

准确描述一下u如果要修改应该要改成c(v)+dep(v)-dep(u)

如果u不用修改则满足c(u)>c(v)+dep(v)-dep(u)

移项之后得c(u)+dep(u)>c(v)+dep(v),即找到第一个满足这个条件的点

那么找到第一个不用修改的u的方法就很显然了,我们处理出倍增数组倍增即可

考虑修改的影响,不难发现一个修改影响到的是一棵子树,如果我们暴力重构子树,时间复杂度就爆炸了

我们可以考虑树分块,将树分成若干个块,我们定义这个块中深度最小的点为这个块的根(可以证明这样的点每个块有且仅有一个)

我们对于每个点维护四个信息

第一个信息是这个点到这个块的根的修改后的序列的和

第二个信息是这个点到根修改后根的权值(不难发现这个信息也满足dp性质)

第三个信息是这个点到根的点权和

第四个信息是这个点向上的倍增数组

考虑每次查询块与块之间的拼接

因为我们记录的第二个信息,当跳到上面的块的时候,我们很容易确定当前点拼接到上一个块之后是否需要修改

如果不需要修改,那么直接继续跳到当前块的根即可

否则我们不难得到当前这个点需要修改到的值,同上述的方法倍增找到第一个不需要修改的点并更新答案即可

最后用修改后的序列的和减去点权和就是答案了

考虑修改的影响

每次修改我们只需要暴力重构块内的信息就可以了

对于第三个信息一遍DFS可以搞定,第四个信息重构倍增数组

第一个和第二个信息利用重构后的倍增数组可以重新计算

这样我们就在O(m*sqrt(n)*log(n))的时间内解决了这个问题

但是常数较小,所以跑得还是很快的

QAQ的LIS树2

听何神说这个题目可以用离线+线段树合并搞一搞,但是蒟蒻智商低,并没有听懂

所以自己YY了一个可以在线的O(nlog^2n)的做法

这个询问比上个询问要好想一点

我们考虑一棵子树怎么样才是合法的,当且仅当这棵子树的所有边都是存在的

但是我们如果用0/1表示存在性的话,查询全局的可行解的最大值就变得非常的麻烦

我们不妨将所有不合法的解都变成不可能在查询最大值时查询到的数值,那么我们就可以直接查询最大值了

做法是这样的,我们给每条边都赋一个大于n的权值,每个点一开始都是自己的子树大小

当一条边从合法变成不合法的时候,我们将这条边上面的所有点都减去这个权值

当一条边从不合法变成合法的时候,我们将这条边上面的所有点都加上这个权值

不难发现,一个点的权值是正整数当且仅当子树内所有边均合法

否则因为sz<=n,只要有一条边不合法,其上面所有的点都会减去一个>n的数值变成负数

这用树链剖分+线段树是易于维护的

那么查询就异常的简单了,我们只需要求一下全局的最大值即可(因为不合法的解不可能作为最优解)

对于修改,我们发现每次修改因为修改的是点权,所以影响到的不只是一条边

但是我们会发现我们可以把影响到的边分成两类,一类是当前点到儿子的边,另一类是当前点到父亲的边

不难发现第一类边虽然可能有很多,但是影响到的点都是一样的

我们只需要算出这些影响的综合就可以了,也就是要查询更改前和更改后不合法的边的权值和

不合法的边一定满足c(v)>=c(u),我们对于每个点以c为键值用平衡树维护儿子的信息即可

这样每次在平衡树上直接查就可以了,顺便更改掉其父亲对应的平衡树

之后再判断当前点到父亲的边是否发生了变化并相应的进行修改就可以了

时间复杂度O(nlog^2n)

贴下代码咯,两道题目二合一强行用namespace凑起来

#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;

typedef long long LL;
const int maxn=100010;
const LL oo=1LL<<60;
int n,m,f,u,v,blo;
int h[maxn],cnt=0;
int fa[maxn],top[maxn],son[maxn];
int w[maxn],pos[maxn],fp[maxn],tot=0;
int co[maxn],rt[maxn];
bool vis[maxn];
int dep[maxn];
struct edge{
	int to,next;
}G[maxn];
LL c[maxn],sz[maxn];
LL add[maxn<<2];
LL mx[maxn<<2];
LL xp[maxn];
void Add(int x,int y){++cnt;G[cnt].to=y;G[cnt].next=h[x];h[x]=cnt;}
void read(int &num){
	num=0;char ch=getchar();
	while(ch<‘!‘)ch=getchar();
	while(ch>=‘0‘&&ch<=‘9‘)num=num*10+ch-‘0‘,ch=getchar();
}
struct Splay_Tree{
	int fa[maxn],C[maxn][2];
	LL s[maxn],val[maxn],V[maxn];
	#define fa(i) fa[i]
	#define c(x,i) C[x][i]
	#define s(i) s[i]
	#define val(i) val[i]
	void up(int u){
		if(!u)return;
		s(u)=s(c(u,0))+s(c(u,1))+val(u);
	}
	void rotate(int p,int x){
		int mark= p==c(x,1),y=c(p,mark^1),z=fa(x);
		if(c(z,0)==x)c(z,0)=p;
		if(c(z,1)==x)c(z,1)=p;
		if(y)fa(y)=x;
		fa(p)=z;c(p,mark^1)=x;fa(x)=p;c(x,mark)=y;
		up(x);
	}
	void Splay(int p,int k,int &rt){
		while(fa(p)!=k){
			int x=fa(p),y=fa(x);
			if(y==k)rotate(p,x);
			else if(p==c(x,0)^x==c(y,0))rotate(p,x),rotate(p,y);
			else rotate(x,y),rotate(p,x);
		}up(p);if(!k)rt=p;
	}
	void insert(int u,int &rt){
		int now=rt,f=now;
		while(now){
			f=now;
			if(c[u]>V[now])now=c(now,1);
			else now=c(now,0);
		}
		fa(u)=f;V[u]=c[u];val(u)=s(u)=co[u];
		if(f)c(f,c[u]>V[f])=u;
		Splay(u,0,rt);
	}
	void del(int u,int &rt){
		Splay(u,0,rt);
		if(!c(u,0)){rt=c(u,1);fa(rt)=0;c(u,1)=0;return;}
		int now=c(u,0);
		while(c(now,1))now=c(now,1);
		Splay(now,u,rt);
		fa(now)=0;c(u,0)=0;rt=now;
		fa(c(u,1))=now;c(now,1)=c(u,1);c(u,1)=0;
		up(now);
	}
	int Get_pre(LL v,int &rt){
		int now=rt,p=-1;
		while(now){
			if(V[now]<v)p=now,now=c(now,1);
			else now=c(now,0);
		}return p;
	}
	LL ask(LL v,int &rt){
		int pre=Get_pre(v,rt);
		if(pre==-1)return s(rt);
		Splay(pre,0,rt);
		return s(c(rt,1));
	}
	void DFS(int u){
		if(!u)return;
		DFS(c(u,0));
		printf("%lld ",V[u]);
		DFS(c(u,1));
	}
}T;
namespace S{
	void DFS(int u,int f){
		w[u]=1;sz[u]=co[u];
		for(int i=h[u];i;i=G[i].next){
			int v=G[i].to;
			if(v==f)continue;
			dep[v]=dep[u]+1;
			DFS(v,u);w[u]+=w[v];sz[u]+=sz[v];
			if(w[son[u]]<w[v])son[u]=v;
		}return;
	}
	void Get_pos(int u,int f){
		top[u]=f;pos[u]=++tot;fp[tot]=u;
		if(!son[u])return;
		Get_pos(son[u],f);
		for(int i=h[u];i;i=G[i].next){
			int v=G[i].to;
			if(v==f||v==son[u])continue;
			Get_pos(v,v);
		}return;
	}
	void Get_Splay(int u,int f){
		for(int i=h[u];i;i=G[i].next){
			int v=G[i].to;
			if(v==f)continue;
			T.insert(v,rt[u]);
			Get_Splay(v,u);
		}return;
	}
	void build(int o,int L,int R){
		if(L==R){mx[o]=sz[fp[L]];return;}
		int mid=(L+R)>>1;
		build(o<<1,L,mid);build(o<<1|1,mid+1,R);
		mx[o]=max(mx[o<<1],mx[o<<1|1]);
	}
	void push_down(int o,int l,int r){
		mx[l]+=add[o];add[l]+=add[o];
		mx[r]+=add[o];add[r]+=add[o];
		add[o]=0;
	}
	void UPD(int o,int L,int R,int x,int y,LL v){
		if(L>=x&&R<=y){
			mx[o]+=v;add[o]+=v;
			return;
		}
		int mid=(L+R)>>1;
		int l=(o<<1),r=(l|1);
		if(add[o]!=0)push_down(o,l,r);
		if(y<=mid)UPD(o<<1,L,mid,x,y,v);
		else if(x>mid)UPD(o<<1|1,mid+1,R,x,y,v);
		else UPD(o<<1,L,mid,x,y,v),UPD(o<<1|1,mid+1,R,x,y,v);
		mx[o]=max(mx[o<<1],mx[o<<1|1]);
	}
	void Get_add(int u,LL v){
		while(u){
			UPD(1,1,n,pos[top[u]],pos[u],v);
			u=fa[top[u]];
		}return;
	}
	void Get_modify(int u,LL v){
		LL now=T.ask(v,rt[u])-T.ask(c[u],rt[u]);
		UPD(1,1,n,pos[u],pos[u],now);
		if(fa[u]){
			if(c[u]>=c[fa[u]])now-=co[u];
			if(v>=c[fa[u]])now+=co[u];
			T.del(u,rt[fa[u]]);c[u]=v;
			T.insert(u,rt[fa[u]]);
			Get_add(fa[u],now);
		}c[u]=v;return;
	}
};
namespace C{
	int anc[maxn][10];LL mx[maxn][20];
	int rt[maxn],sz[maxn],dep[maxn];
	LL go[maxn],co[maxn],s[maxn];
	void Get_block(int u,int f){
		if(sz[rt[f]]==blo)rt[u]=u,sz[u]=1;
		else sz[rt[f]]++,rt[u]=rt[f];
		for(int i=h[u];i;i=G[i].next){
			int v=G[i].to;
			if(v==f)continue;
			dep[v]=dep[u]+1;
			Get_block(v,u);
		}return;
	}
	void Get_pre(int u,int v){
		int now=dep[u]-dep[v];
		anc[u][0]=fa[u];mx[u][0]=c[u]+dep[u];
		for(int i=1;(1<<(i-1))<=now;++i){
			if(anc[u][i-1]!=-1){
				int a=anc[u][i-1];
				anc[u][i]=anc[a][i-1];
				mx[u][i]=max(mx[u][i-1],mx[a][i-1]);
			}
		}return;
	}
	int Get_mx(int u,int v,int w){
		int log,now=dep[u]-dep[v];
		for(log=0;(1<<log)<=now;++log);--log;
		for(int i=log;i>=0;--i){
			if(anc[u][i]!=-1&&mx[u][i]<=w)u=anc[u][i];
		}
		if(c[u]+dep[u]<=w)return -1;
		if(dep[u]<dep[v])return -1;
		return u;
	}
	void Get_ans(int u){
		int p=Get_mx(u,rt[u],c[u]+dep[u]);
		if(p==-1){
			go[u]=c[u]*(dep[u]-dep[rt[u]]+1)+xp[dep[u]-dep[rt[u]]];
			co[u]=c[u]+dep[u]-dep[rt[u]];
		}else{
			go[u]=go[p]+c[u]*(dep[u]-dep[p])+xp[dep[u]-dep[p]-1];
			co[u]=co[p];
		}return;
	}
	void DFS_block(int u,int f){
		Get_pre(u,rt[u]);Get_ans(u);
		if(rt[f]==rt[u])s[u]=s[f]+c[u];
		else s[u]=c[u];
		for(int i=h[u];i;i=G[i].next){
			int v=G[i].to;
			if(v==f||rt[v]!=rt[u])continue;
			DFS_block(v,u);
		}return;
	}
	LL Get_cost(int u){
		LL ans=go[u]-s[u],la=co[u];
		u=fa[rt[u]];
		while(u){
			if(la<c[u])ans=ans+go[u],la=co[u];
			else{
				int p=Get_mx(u,rt[u],la+1+dep[u]);
				if(p==-1){
					ans=ans+(la+1)*(dep[u]-dep[rt[u]]+1)+xp[dep[u]-dep[rt[u]]];
					la=la+1+dep[u]-dep[rt[u]];
				}else{
					ans=ans+go[p]+(la+1)*(dep[u]-dep[p])+xp[dep[u]-dep[p]-1];
					la=co[p];
				}
			}ans-=s[u];u=fa[rt[u]];
		}return ans;
	}
};

int main(){
	freopen("increasing.in","r",stdin);
	freopen("increasing.out","w",stdout);
	int __size__=128<<20;
	char *__p__=(char*)malloc(__size__)+__size__;
	__asm__("movl %0, %%esp\n"::"r"(__p__));
	read(n);blo=150;
	memset(C::anc,-1,sizeof(C::anc));
	xp[0]=0;
	for(int i=1;i<=n;++i)xp[i]=xp[i-1]+i;
	for(int i=2;i<=n;++i){
		read(f);f++;
		Add(f,i);fa[i]=f;
	}
	for(int i=1;i<=n;++i)co[i]=rand()%n+n,co[i]=-co[i];
	S::DFS(1,-1);S::Get_pos(1,1);
	for(int i=1;i<=n;++i)sz[i]+=w[i],sz[i]-=co[i];
	S::build(1,1,n);S::Get_Splay(1,-1);
	C::sz[0]=blo;C::Get_block(1,0);
	for(int i=1;i<=n;++i)if(C::rt[i]==i)C::DFS_block(i,fa[i]);
	read(m);
	while(m--){
		char ch=getchar();
		while(ch<‘!‘)ch=getchar();
		if(ch==‘M‘){
			read(u);read(v);u++;
			S::Get_modify(u,c[u]+v);
			C::DFS_block(u,fa[u]);
		}else if(ch==‘S‘)printf("%lld\n",mx[1]);
		else{
			read(u);u++;
			printf("%lld\n",C::Get_cost(u));
		}
	}return 0;
}

  

时间: 2024-10-07 15:11:50

QAQ的LIS树 QAQ的LIS树2 题解报告的相关文章

[bzoj3932][CQOI2015]任务查询系统-题解[主席树][权值线段树]

Description 最近实验室正在为其管理的超级计算机编制一套任务管理系统,而你被安排完成其中的查询部分.超级计算机中的 任务用三元组(Si,Ei,Pi)描述,(Si,Ei,Pi)表示任务从第Si秒开始,在第Ei秒后结束(第Si秒和Ei秒任务也在运行 ),其优先级为Pi.同一时间可能有多个任务同时执行,它们的优先级可能相同,也可能不同.调度系统会经常向 查询系统询问,第Xi秒正在运行的任务中,优先级最小的Ki个任务(即将任务按照优先级从小到大排序后取前Ki个 )的优先级之和是多少.特别的,如

【BZOJ】1047: [HAOI2007]理想的正方形(单调队列/~二维rmq+树状数组套树状数组)

http://www.lydsy.com/JudgeOnline/problem.php?id=1047 树状数组套树状数组真心没用QAQ....首先它不能修改..而不修改的可以用单调队列做掉,而且更快,只有O(n^2).而这货是n^2log^2n的建树...虽然查询是log^2n...但是建树那里就tle了.. 那么说题解... 先orz下,好神.. 我怎么没想到单调队列orz 首先我们维护 行 的单调队列,更新每个点在 列 距离内的最小and最大的值 然后我们维护 列 的单调队列,更新每个点

跳跃表,字典树(单词查找树,Trie树),后缀树,KMP算法,AC 自动机相关算法原理详细汇总

第一部分:跳跃表 本文将总结一种数据结构:跳跃表.前半部分跳跃表性质和操作的介绍直接摘自<让算法的效率跳起来--浅谈"跳跃表"的相关操作及其应用>上海市华东师范大学第二附属中学 魏冉.之后将附上跳跃表的源代码,以及本人对其的了解.难免有错误之处,希望指正,共同进步.谢谢. 跳跃表(Skip List)是1987年才诞生的一种崭新的数据结构,它在进行查找.插入.删除等操作时的期望时间复杂度均为O(logn),有着近乎替代平衡树的本领.而且最重要的一点,就是它的编程复杂度较同类

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

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

从B树、B+树、B*树谈到R 树

第一节.B树.B+树.B*树 1.前言: 动态查找树主要有:二叉查找树(Binary Search Tree),平衡二叉查找树(Balanced Binary Search Tree),红黑树(Red-Black Tree ),B-tree/B+-tree/ B*-tree(B~Tree).前三者是典型的二叉查找树结构,其查找的时间复杂度O(log2N)与树的深度相关,那么降低树的深度自然会提高查找效率. 但是咱们有面对这样一个实际问题:就是大规模数据存储中,实现索引查询这样一个实际背景下,树节

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

hdu 5412 CRB and Queries(线段树套笛卡尔树 - 动态区间第k大)

题目链接:hdu 5412 CRB and Queries 首先对所有出现过的值排序,建立线段树,每个线段树的节点是一棵笛卡尔树,笛卡尔树记录区间下标值. #include <cstdio> #include <cstring> #include <cstdlib> #include <algorithm> using namespace std; #define lson(x) (x<<1) #define rson(x) ((x<<

cojs 疯狂的魔法树 疯狂的颜色序列 题解报告

疯狂的魔法树 一个各种操作大杂烩的鬼畜数据结构题目 首先我们注意到树的形态是半随机的 我们可以树分块,对树分成若干个块 对于每个块我们维护一个add标记表示增量 维护一个vis标记表示覆盖量 注意标记的下放和两个标记之间的处理 之后我们对于每个块我们排序,并维护块内的有序化 这样对于每个查询如果涉及到完整的块二分即可 否则暴力查询 这样时间复杂度是O(n*sqrt(n)*log(n)) 疯狂的颜色序列 很古老的题目,如果去掉强制在线的话我已知的有五种不同的解法 第一种做法是莫队 第二种做法是离线