【codeforces】482E. ELCA 动态树

传送门:【codeforces】482E. ELCA


题目大意:给一棵有根树,树根为1,每个节点有权值s[v],q次操作,每次要么将以v为根的子树接到u下,要么令s[v]=t,每次操作以后,输出等概率选择两个点i,j(i可以等于$)后权值的期望,权值即s[{lca(i,j)}]值。


题目分析:写了两天啊啊啊啊!!!终于搞出来了。。。思考这题算法的时候总感觉脑子不够用啊= =于是好不容易AC了一次就再写了一发。。

这题一看就知道是动态树问题,但关键的是怎么转化,比较好的方法是每个节点x保存以这个节点为根结点的子树中LCA为x的路径种数,x的子树大小tree_siz,x在splay中不包括splay树中右儿子tree_siz的siz,这样在处理的过程中改变access函数,每次access一定是将真实树中的树边扩充为splay边,然后很巧妙的就可以转化为路径求和问题。具体只能见代码= =我真的描述不清楚。。。

1.注意cut以及link操作是怎么执行的。

2.注意tree_siz只与真实树有关,所以除了cut及link涉及的修改路径操作,splay的其他操作都对他没有影响。

3.注意siz只在access的时候发生变化。


代码如下:


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

typedef long long LL ;

#define rep( i , a , b ) for ( int i = ( a ) ; i <  ( b ) ; ++ i )
#define For( i , a , b ) for ( int i = ( a ) ; i <= ( b ) ; ++ i )
#define rev( i , a , b ) for ( int i = ( a ) ; i >= ( b ) ; -- i )
#define clr( a , x ) memset ( a , x , sizeof a )

const int MAXN = 50005 ;
const int MAXE = 100005 ;

struct Node* null ;
struct Node {
	Node* c[2] ;
	Node* f ;
	LL cnt ;
	LL sum ;
	int val ;
	int siz ;
	int lazy ;
	int tree_siz ;
	inline void new_node () {
		sum = val = 0 ;
		c[0] = c[1] = f = null ;
		lazy = 0 ;
	}
	inline void add_siz ( int SIZE ) {
		if ( this == null ) return ;
		tree_siz += SIZE ;
		cnt += 2LL * siz * SIZE ;
		lazy += SIZE ;
	}
	inline void push_up () {
		sum = c[0]->sum + ( LL ) siz * val + c[1]->sum ;
	}
	inline void push_down () {
		if ( lazy ) {
			c[0]->add_siz ( lazy ) ;
			c[1]->add_siz ( lazy ) ;
			lazy = 0 ;
		}
	}
	inline bool is_root () {
		return f == null || f->c[0] != this && f->c[1] != this ;
	}
	inline void sign_down () {
		if ( !is_root () ) f->sign_down () ;
		push_down () ;
	}
	inline void setc ( Node* o , bool d ) {
		c[d] = o ;
		o->f = this ;
	}
	inline void rot ( bool d ) {
		Node* p = f ;
		Node* g = f->f ;
		p->setc ( c[d] , !d ) ;
		if ( !p->is_root () ) g->setc ( this , p == g->c[1] ) ;
		else f = g ;
		setc ( p , d ) ;
		p->push_up () ;
	}
	inline void splay () {
		sign_down () ;
		while ( !is_root () ) {
			if ( f->is_root () ) rot ( this == f->c[0] ) ;
			else {
				if ( f == f->f->c[0] ) {
					if ( this == f->c[0] ) f->rot ( 1 ) , rot ( 1 ) ;
					else rot ( 0 ) , rot ( 1 ) ;
				} else {
					if ( this == f->c[1] ) f->rot ( 0 ) , rot ( 0 ) ;
					else rot ( 1 ) , rot ( 0 ) ;
				}
			}
		}
		push_up () ;
	}
	inline void access () {
		Node* o = this ;
		Node* x = null ;
		while ( o != null ) {
			if ( x != null ) {
				while ( x->c[0] != null ) x = x->c[0] ;
				x->splay () ;
			}
			o->splay () ;
			o->siz = o->tree_siz - x->tree_siz ;//without splay_tree right_son tree_siz
			o->setc ( x , 1 ) ;
			o->push_up () ;
			x = o ;
			o = o->f ;
		}
		splay () ;
	}
} ;

struct Edge {
	int v ;
	Edge* next ;
} ;

Node pool[MAXN] ;
Node* node[MAXN] ;
Node* curN ;
Edge edge[MAXE] ;
Edge* H[MAXN] ;
Edge* curE ;
LL ans ;
int n , m ;

void clear () {
	ans = 0 ;
	curE = edge ;
	curN = pool ;
	null = curN ++ ;
	null->new_node () ;
	null->cnt = null->tree_siz = null->siz = 0 ;
	clr ( H , 0 ) ;
}

void addedge ( int u , int v ) {
	curE->v = v ;
	curE->next = H[u] ;
	H[u] = curE ++ ;
}

void dfs ( int u ) {
	node[u]->cnt = node[u]->tree_siz = 1 ;
	for ( Edge* e = H[u] ; e ; e = e->next ) {
		int v = e->v ;
		dfs ( v ) ;
		node[u]->cnt += 2LL * node[u]->tree_siz * node[v]->tree_siz ;
		node[u]->tree_siz += node[v]->tree_siz ;
	}
	ans += node[u]->cnt * node[u]->val ;//calculate init ans
	node[u]->siz = node[u]->tree_siz ;
}

void solve () {
	int x , y ;
	char op[2] ;
	double tot = ( double ) n * n ;
	clear () ;
	For ( i , 1 , n ) {
		node[i] = curN ++ ;
		node[i]->new_node () ;
	}
	For ( i , 2 , n ) {
		scanf ( "%d" , &x ) ;
		addedge ( x , i ) ;
		node[i]->f = node[x] ;
	}
	For ( i , 1 , n ) scanf ( "%d" , &node[i]->val ) ;
	dfs ( 1 ) ;
	printf ( "%.10f\n" , ans / tot ) ;
	scanf ( "%d" , &m ) ;
	while ( m -- ) {
		scanf ( "%s%d%d" , op , &x , &y ) ;
		if ( op[0] == 'P' ) {
			node[y]->access () ;
			node[x]->splay () ;
			if ( node[x]->f == null ) swap ( x , y ) ;
			//cut
			node[x]->access () ;
			Node* o = node[x]->c[0] ;
			ans -= 2LL * o->sum * node[x]->tree_siz ;
			o->add_siz ( -node[x]->tree_siz ) ;
			node[x]->c[0]->f = null ;
			node[x]->c[0] = null ;
			node[x]->push_up () ;
			o->push_up () ;
			//link
			node[y]->access () ;
			ans += 2LL * node[y]->sum * node[x]->tree_siz ;
			node[y]->add_siz ( node[x]->tree_siz ) ;
			node[y]->push_up () ;
			node[x]->f = node[y] ;
		} else {
			node[x]->access () ;
			ans += node[x]->cnt * ( y - node[x]->val ) ;
			node[x]->val = y ;
			node[x]->push_up () ;
		}
		printf ( "%.10f\n" , ans / tot ) ;
	}
}

int main () {
	while ( ~scanf ( "%d" , &n ) ) solve () ;
	return 0 ;
}


压缩后代码:

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
#define clr(a,x) memset(a,x,sizeof a)
const int MAXN=50005;
const int MAXE=100005;
struct Node *null;
struct Node{
	Node *c[2],*f;
	LL cnt,sum;
	int val,siz,lazy,tree_siz;
	inline void new_node(){sum=val=lazy=0,c[0]=c[1]=f=null;}
	inline void add_siz(int SIZE){
		if(this==null)return;
		tree_siz+=SIZE;
		cnt+=2LL*siz*SIZE;
		lazy+=SIZE;
	}
	inline void up(){sum=c[0]->sum+(LL)siz*val+c[1]->sum;}
	inline void down(){if(lazy)c[0]->add_siz(lazy),c[1]->add_siz(lazy),lazy=0;}
	inline bool is_root(){return f==null||f->c[0]!=this&&f->c[1]!=this;}
	inline void sign_down(){if(!is_root())f->sign_down();down();}
	inline void setc(Node *o,bool d){c[d]=o,o->f=this;}
	inline void rot(bool d){
		Node *p=f,*g=f->f;
		p->setc(c[d],!d);
		if(!p->is_root())g->setc(this,p==g->c[1]);
		else f=g;
		setc(p,d);
		p->up();
	}
	inline void splay(){
		for(sign_down();!is_root();){
			if(f->is_root())rot(this==f->c[0]);
			else{
				if(f==f->f->c[0])this==f->c[0]?f->rot(1):rot(0),rot(1);
				else this==f->c[1]?f->rot(0):rot(1),rot(0);
			}
		}
		up();
	}
	inline void access(){
		for(Node*o=this,*x=null;o!=null;x=o,o=o->f){
			if(x!=null){
				while(x->c[0]!=null)x=x->c[0];
				x->splay();
			}
			o->splay();
			o->siz=o->tree_siz-x->tree_siz;//withoutsplay_treeright_sontree_siz
			o->setc(x,1);
			o->up();
		}
		splay();
	}
}pool[MAXN],*node[MAXN],*curN;;
struct Edge{
	int v;
	Edge *next;
}edge[MAXE],*H[MAXN],*curE;
LL ans;
int n,m;
void init(){
	ans=0;
	curE=edge;
	curN=pool;
	null=curN++;
	null->new_node();
	null->cnt=null->tree_siz=null->siz=0;
	clr(H,0);
}
void addedge(int u,int v){
	curE->v=v;
	curE->next=H[u];
	H[u]=curE++;
}
void dfs(int u){
	node[u]->cnt=node[u]->tree_siz=1;
	for(Edge *e=H[u];e;e=e->next){
		int v=e->v;
		dfs(v);
		node[u]->cnt+=2LL*node[u]->tree_siz*node[v]->tree_siz;
		node[u]->tree_siz+=node[v]->tree_siz;
	}
	ans+=node[u]->cnt*node[u]->val;//calculateinitans
	node[u]->siz=node[u]->tree_siz;
}
void solve(){
	int x,y,i;
	char op[2];
	double tot=(double)n*n;
	init();
	for(i=1;i<=n;++i){
		node[i]=curN++;
		node[i]->new_node();
	}
	for(i=2;i<=n;++i){
		scanf("%d",&x);
		addedge(x,i);
		node[i]->f=node[x];
	}
	for(i=1;i<=n;++i)scanf("%d",&node[i]->val);
	dfs(1);
	printf("%.10f\n",ans/tot);
	scanf("%d",&m);
	while(m--){
		scanf("%s%d%d",op,&x,&y);
		if(op[0]=='P'){
			node[y]->access();
			node[x]->splay();
			if(node[x]->f==null)swap(x,y);
			//cut
			node[x]->access();
			Node *o=node[x]->c[0];
			ans-=2LL*o->sum*node[x]->tree_siz;
			o->add_siz(-node[x]->tree_siz);
			node[x]->c[0]->f=null;
			node[x]->c[0]=null;
			node[x]->up();
			o->up();
			//link
			node[y]->access();
			ans+=2LL*node[y]->sum*node[x]->tree_siz;
			node[y]->add_siz(node[x]->tree_siz);
			node[y]->up();
			node[x]->f=node[y];
		}else{
			node[x]->access();
			ans+=node[x]->cnt*(y-node[x]->val);
			node[x]->val=y;
			node[x]->up();
		}
		printf("%.10f\n",ans/tot);
	}
}
int main(){
	while(~scanf("%d",&n))solve();
	return 0;
}



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值