首页 > 其他分享 >Codeforces 1740H - MEX Tree Manipulation

Codeforces 1740H - MEX Tree Manipulation

时间:2023-07-14 10:22:34浏览次数:29  
标签:int siz 点权 Tree wson dat MAXN Manipulation MEX

首先发现一个性质,那就是每个点的点权是 \(\log n\) 级别的。因为假设要造出一个点权为 \(i\) 的点至少需要大小为 \(mn_i\) 的子树,那么显然有 \(mn_i=\sum\limits_{j=0}^{i-1}mn_j+1\),即 \(mn_i=2^i\)。

由于点权不是很大,因此我们很容易地往变换复合的角度思考。将整棵树进行轻重链剖分,然后对于每个点维护一个变换 \(p_i\),表示如果重儿子点权为 \(i\),那么这个点的点权就会变成 \(p_i\),这样查询单点点权就直接将重链底部到当前点上的变换进行一遍复合即可,用线段树维护,类似于 DDP。计算点权和的话就额外在线段树上每个节点上记录一下 \(sum_i\) 表示如果初始值为 \(i\) 那么经过这些变换复合后得到的所有时刻的值之和是多少。

有不少细节需要考虑,比方说没有加入的点的变换设成什么。

const int MAXN=3e5;
int n,fa[MAXN+5],hd[MAXN+5],to[MAXN+5],nxt[MAXN+5],ec;
void adde(int u,int v){to[++ec]=v;nxt[ec]=hd[u];hd[u]=ec;}
int siz[MAXN+5],wson[MAXN+5],cnt[MAXN+5][25],bot[MAXN+5];
int top[MAXN+5],dfn[MAXN+5],rid[MAXN+5],tim;
ll res=0;
void dfs1(int x){
	siz[x]=1;
	for(int e=hd[x];e;e=nxt[e]){
		int y=to[e];dfs1(y);siz[x]+=siz[y];
		if(siz[y]>siz[wson[x]])wson[x]=y;
	}
}
void dfs2(int x,int tp){
	top[x]=tp;rid[dfn[x]=++tim]=x;if(wson[x])dfs2(wson[x],tp);
	for(int e=hd[x];e;e=nxt[e])if(to[e]!=wson[x])dfs2(to[e],to[e]);
}
struct dat{
	int p[25],sum[25];
	dat(){memset(p,0,sizeof(p));memset(sum,0,sizeof(sum));}
	friend dat operator +(const dat &X,const dat &Y){
		dat res;
		for(int i=0;i<=21;i++)res.p[i]=Y.p[X.p[i]],res.sum[i]=X.sum[i]+Y.sum[X.p[i]];
		return res;
	}
}A,B;
struct node{int l,r;dat v;}s[MAXN*4+5];
void build(int k,int l,int r){
	s[k].l=l;s[k].r=r;s[k].v=A;if(l==r)return;
	int mid=l+r>>1;build(k<<1,l,mid);build(k<<1|1,mid+1,r);
}
void modify(int k,int p,dat v){
	if(s[k].l==s[k].r)return s[k].v=v,void();
	int mid=s[k].l+s[k].r>>1;
	if(p<=mid)modify(k<<1,p,v);else modify(k<<1|1,p,v);
	s[k].v=s[k<<1|1].v+s[k<<1].v;
}
pii query(int k,int l,int r,int x){
	if(l<=s[k].l&&s[k].r<=r)return mp(s[k].v.p[x],s[k].v.sum[x]);
	int mid=s[k].l+s[k].r>>1;
	if(r<=mid)return query(k<<1,l,r,x);
	else if(l>mid)return query(k<<1|1,l,r,x);
	else{
		pii p1=query(k<<1|1,mid+1,r,x),p2=query(k<<1,l,mid,p1.fi);
		return mp(p2.fi,p1.se+p2.se);
	}
}
int ask_sum(int x,int y){
	int sum=query(1,dfn[y],dfn[bot[y]],21).se;
	if(wson[x])sum-=query(1,dfn[wson[x]],dfn[bot[wson[x]]],21).se;
	return sum;
}
int ask_val(int x){return query(1,dfn[x],dfn[bot[x]],21).fi;}
pii cur[MAXN+5];
void upd(int x){
	dat d;int fs=-1,sc=-1;
	for(int i=0;i<=21;i++)if(!cnt[x][i]){if(!~fs)fs=i;else if(!~sc)sc=i;}
	for(int i=0;i<=21;i++)d.sum[i]=d.p[i]=(i==fs)?sc:fs;
	if(mp(sc,fs)!=cur[x])cur[x]=mp(sc,fs),modify(1,dfn[x],d);
}
int main(){
	scanf("%d",&n);++n;
	for(int i=0;i<=21;i++)A.p[i]=21;
	for(int i=2;i<=n;i++)scanf("%d",&fa[i]),adde(fa[i],i);
	dfs1(1);dfs2(1,1);build(1,1,n);
	modify(1,dfn[1],B);
	for(int i=1;i<=n;i++)if(top[i]==i){
		int cur=i;
		while(wson[cur])cur=wson[cur];
		bot[i]=cur;
	}
	for(int i=1;i<=n;i++)if(!bot[i])bot[i]=bot[top[i]];
	for(int i=2;i<=n;i++){
		int x=fa[i];
		while(x){
			res-=ask_sum(x,top[x]);
			if(fa[top[x]])cnt[fa[top[x]]][ask_val(top[x])]--;
			x=fa[top[x]];
		}
		if(wson[fa[i]]!=i)cnt[fa[i]][0]++;x=fa[i];modify(1,dfn[i],B);
		while(x){
			upd(x);res+=ask_sum(x,top[x]);
			if(fa[top[x]])cnt[fa[top[x]]][ask_val(top[x])]++;
			x=fa[top[x]];
		}
		print(res,'\n');
	}print_final();
	return 0;
}

标签:int,siz,点权,Tree,wson,dat,MAXN,Manipulation,MEX
From: https://www.cnblogs.com/tzcwk/p/Codeforces-1740H.html

相关文章

  • CF1290E Cartesian Tree 注意点--zhengjun
    解题思路容易想到从小到大加数,维护每个点的子树大小。可转化为维护每个点为\(\max\)时的\([L,R]\)区间。然后需要写一个支持【区间+1】、【区间取min】、单点加入、全局查询。上个吉司机线段树即可。注意点吉司机线段树下推\(fi\)的标记的时候要注意\(fi\)的变化......
  • SegmentTree2
    线段树完全版关键词:延迟加载、懒标记LazyTag单点更新的情况比较简单。请看线段树基础版下面说说区间更新的情况。场景是这样的,还是刚刚的数,求区间的和。准备工作//rt:root#definelsonrt<<1#definersonrt<<1|1#definelen(r-l+1)//(l,r)区间的长度这次是区间更新......
  • 【DP】DMOPC '21 Contest 8 P5 - Tree Building
    ProblemLink给定\(n,m\)和一个长为\(m\)的代价序列,对于一棵\(n\)个节点,每个节点度数不超过\(m\)的树,定义它的代价为\(\sum\limits_{i=1}^na_{deg_i}\)。求代价最小的树的代价。\(n\le10^9,m\le3000,1\lea_i\le10^9\)。首先一眼变成选出\(n\)个\(a\)的和为......
  • CF1846D Rudolph and Christmas Tree 题解
    Decription一颗圣诞树由\(n\)个底边为\(d\),高度为\(h\)的等腰三角形组成,每个三角形以\(y\)轴为对称轴,底边均平行于\(x\)轴,三角形有可能重叠。给出\(n,d,h\)以及每个三角形底边与\(x\)轴的距离,求该圣诞树的面积。Solution如图,这是一棵圣诞树,其由两部分组成,完整......
  • git-worktree
    1.说明git-worktreegitworktree非常适合大型项目又需要维护多个分支,想要避免来回切换的情况优点gitworktree可以快速进行并行开发,同一个项目多个分支同时并行演进gitworktree的提交可以在同一个项目中共享gitworktree和单独clone项目相比,节省了硬盘空间,......
  • react-d3-tree自定义节点使用案例
    react-d3-tree主要API及其中文解释:Tree组件的props:这些API提供了丰富的配置选项,可以用来定制树的外观和行为。例如,可以使用nodeSize属性调整节点的大小,使用pathFunc属性绘制自定义的连线,使用onClick属性处理节点的点击事件等等。data:树的数据对象。zoomable:指......
  • DevExpress WinForms TreeList控件,让业务数据展示更清晰!(一)
    DevExpressWinForms的TreeList控件是一个功能齐全、数据感知的TreeView-ListView的混合体,它可以以树形、网格或两者结合的形式显示数据信息。无论是数据绑定模式还是非绑定模式,都具有完整的数据编辑支持。PS:DevExpressWinForm拥有180+组件和UI库,能为WindowsForms平台创建具有......
  • CF884G Tree Wights
    CF884GTreeWights给定一棵\(n\)个点的树,给定\(d_1,d_2,\cdots,d_{n-1}\),其中\(d_i\)表示\(i\)到\(i+1\)在树上简单路径的距离,问能否构造每条边的边权都是正整数,并构造一种解。\(2\len\le10^5\),\(1\led_i\le10^{12}\)。本题难点在于两次转化。首先,如果题目中......
  • Java TreeMap 介绍与使用
    介绍TreeMap是Java集合框架中的一个类,它实现了SortedMap接口,可以存储键值对,并按照键的自然顺序或者指定的比较器进行排序。TreeMap的底层是一棵红黑树,这是一种自平衡的二叉搜索树,可以保证在插入,删除,查找等操作中的时间复杂度为O(logn)。使用要使用TreeMap,我们需要导入......
  • AtCoder Regular Contest 164 E Segment-Tree Optimization
    洛谷传送门AtCoder传送门妙妙题。我们考虑如何方便地描述一棵广义线段树。不难想到使用断点描述,即对于一个结点\([l,r]\),它的左右儿子分别是\([l,mid],[mid+1,r]\),其中\(mid\in[l,r-1]\),然后把一层缩成一个\(2^{d-1}\)的序列,每一层都是上层对应结点的\(mid......