zoukankan      html  css  js  c++  java
  • 城市旅行

    题目描述

    (W) 国地人物博,有 (n) 座城市组成,共 (n-1) 条双向道路连接其中的两座城市,且任意两座城市都可相互到达。
    风景秀美的 (w) 国吸引了无数慕名而来的游客,根据游客对每座城市的打分,我们定义第 (i) 座城市的美丽度为 (a_i)。一次从城市 (x) 到城市 (y) 的旅行,所获得的的偷悦指数为从城市 (x) 到 城市 (y) 所有城市的美丽度之和(包括(x)(y))。我们记这个值为 (H(x,y))
    现在小A在城市 (X),Sharon在城市 (Y),他们想知道如果在城市 (X) 到城市 (Y) 之间的所有城市中任选两座城市 (x)(y) ((x)可以等于(y)),那么 (H(x,y)) 的期望值是多少,我们记这个期望值 (E(x,y))
    当然,城市之间的交通状况飘忽不定,因此我们不能排除某些时刻某些道路将无法通行。某些时刻会突然添加新的道路。以及游客们审美观的改变,某些城市的美丽度也会发生变化。作为 (W) 国负责旅游行业的T君,他要求你来写一个程序来模拟上而的所有过程。

    输入格式

    第一行两个整数,(n,m) 表示城市个数和操作个数。

    接下来一行 (n) 个整数,第 (i) 个表示 (a_i) 。 接下来 (n-1) 行,每行两个整数 (u,v),表示 (u)(v) 之间有一条路。 接下来 (m) 行,是进行下面的操作:
    1 u v 如果城市 (u) 和城市 (v) 已经无直接连接的道路,则忽略这个操作,否则删除 (u,v) 之间的道路。
    2 u v 如果城市 (u) 和城市 (v) 联通那么忽略。否则在 (u,v) 之间添加一条道路。
    3 u v d 如果城市 (u) 和城市 (v) 不连通,那么忽略。否则将城市 (u) 到城市 (v) 的路径中所有城市(包括(u)(v))的美丽度都增加d。

    4 u v 询问 (E(u,v)) 的值

    输出格式

    对于操作4,输出答案,一个经过化简的分数 (p/q) 。如果 (u)(v) 不连通输出 (-1)

    放在数学专题里的毒瘤数据结构题

    首先发现这是个假期望,最后分母其实就是 (inom{len}{2})

    然后前三个操作都是lct的基础操作,可以直接维护

    现在我们考虑怎么得到一条路径上的所有答案和

    先考虑如果在一个序列上 ([l,r]) 的答案就是 (sumlimits_{i=l}^ra_i imes (i-l+1) imes (r-i+1))

    转化到树上,对于询问 u v 考虑如果使 (v) 为树根,那么答案为 (sumlimits_{iin u->v }a_i imes dep_i imes (dep_x-dep_i+1))

    拆一下(sumlimits_{iin u->v }a_i imes dep_i imes (dep_x-dep_i+1)=sumlimits_{iin u->v}a_i imes dep_i(dep_x+1)-a_i imes dep_i^2)

    于是我们需要维护 (da[x]=sumlimits_{iin x} a_i imes dep_i)(da2[x]=sumlimits_{iin x} a_i imes dep_i^2)

    考虑如何合并两个子树的信息

    首先左子树的答案不变可以直接加上,考虑右子树答案的变化

    由于我们统计答案时一定是一条链,那么右子树每个点的 (dep) 都应该增加 (sz[lc]+1)

    那么

    [egin{aligned} da[x]&=da[lc]+(sz[lc]+1) imes a[x]+sumlimits_{iin rc}(sz[lc]+1+dep[i]') imes a[i]\ &=da[lc]+da[rc]+(a[x]+sum[rc]) imes (sz[lc]+1) \ da2[x]&=da2[lc]+(sz[lc]+1)^2 imes a[x]+sumlimits_{iin rc}(sz[lc]+1+dep[i]')^2 imes a[i]\ &=da2[lc]+(sz[lc]+1)^2 imes a[x]+sumlimits_{iin rc}((sz[lc]+1)^2+2 imes(sz[lc]+1) imes dep[i]'+dep[i]'^2) imes a[i]\ &=da2[lc]+da2[rc]+(sum[rc]+a[x]) imes (sz[lc]+1)^2+2(sz[lc]+1) imes sum_{iin rc}dep[i]'a[i]\ &=da2[lc]+da2[rc]+(sum[rc]+a[x]) imes (sz[lc]+1)^2+2 imes da[rc] imes (sz[lc]+1) end{aligned}]

    再考虑修改,考虑一条链所有点权值增加 (d) 造成的增量,有

    [egin{aligned} Delta da[x]&=sum_{iin x}dep[i] imes d\ &=frac{sz[x] imes(sz[x]+1)}{2} imes d\ Delta da2[x]&=sum_{iin x}dep[i]^2 imes d\ &=frac{sz[x] imes(sz[x]+1) imes(sz[x] imes 2+1)}{6} imes d end{aligned}]

    完了?不不不,我们发现 (makeroot) 的时候整棵树的深度会翻转,这样的话每个点的 (dep) 也会改变

    但是我们发现改变只是 (dep) 从原来左儿子的 (sz) 变成了原来右儿子的 (sz)

    那么我们只需要维护两套 (da)(da2),翻转的时候交换他们就行了

    链加还需要维护懒标记

    总共需要维护 (lda,lda2,rda,rda2,sz,a,lazy 7) 个变量

    (link)(cut) 之前需要先下传标记(具体实现只要在操作之前 (split) 一下)

    code
    
    #include<bits/stdc++.h>
    #define ll long long
    #define cri const register int
    #define re register
    #define int ll
    #define lc c[x][0]
    #define rc c[x][1]
    using namespace std;
    int c[50010][2],la[50010],l2a[50010],sz[50010],ra[50010],r2a[50010],lz[50010],a[50010];
    int f[50010],r[50010],sta[50010],sum[50010];
    inline bool nrt(cri x){ return c[f[x]][0]==x||c[f[x]][1]==x; }
    inline void up(cri x){
    	sz[x]=sz[lc]+sz[rc]+1;
    	sum[x]=sum[lc]+sum[rc]+a[x];
    	la[x]=(a[x]+sum[rc])*(sz[lc]+1)+la[lc]+la[rc];
    	ra[x]=(a[x]+sum[lc])*(sz[rc]+1)+ra[lc]+ra[rc];
    	l2a[x]=(a[x]+sum[rc])*(sz[lc]+1)*(sz[lc]+1)+l2a[lc]+l2a[rc]+la[rc]*(sz[lc]+1)*2;
    	r2a[x]=(a[x]+sum[lc])*(sz[rc]+1)*(sz[rc]+1)+r2a[lc]+r2a[rc]+ra[lc]*(sz[rc]+1)*2;
    }
    inline void fz(cri x){
    	r[x]^=1;
    	swap(lc,rc);
    	swap(la[x],ra[x]);swap(l2a[x],r2a[x]);
    }
    inline void add(cri x,cri y){
    	lz[x]+=y; a[x]+=y;
    	sum[x]+=sz[x]*y;
    	la[x]+=sz[x]*(sz[x]+1)/2*y;
    	ra[x]+=sz[x]*(sz[x]+1)/2*y;
    	l2a[x]+=sz[x]*(sz[x]+1)*(sz[x]<<1|1)/6*y;
    	r2a[x]+=sz[x]*(sz[x]+1)*(sz[x]<<1|1)/6*y;
    }
    inline void down(cri x){
    	if(lc){
    		add(lc,lz[x]);
    		if(r[x]) fz(lc);
    	}
    	if(rc){
    		add(rc,lz[x]);	
    		if(r[x]) fz(rc);
    	}
    	r[x]=lz[x]=0;
    }
    inline void rotate(cri x){
    	cri y=f[x],z=f[y],k=c[y][1]==x,w=c[x][k^1];
    	if(nrt(y)) c[z][c[z][1]==y]=x;
    	c[x][k^1]=y,c[y][k]=w;
    	if(w) f[w]=y; f[y]=x; f[x]=z;
    	up(y);up(x);
    }
    inline void splay(int x){
    	int z=0,y=x;
    	sta[++z]=y;
    	while(nrt(y)) sta[++z]=y=f[y];
    	while(z) down(sta[z--]);
    	while(nrt(x)){
    		y=f[x],z=f[y];
    		if(nrt(y)) rotate(c[z][1]==y^c[y][1]==x?x:y);
    		rotate(x);
    	}
    }
    inline void ac(int x){
    	for(int y=0;x;x=f[y=x]) splay(x),rc=y,up(x);
    }
    inline int mrt(cri x){
    	ac(x);splay(x);fz(x);
    }
    inline int frt(int x){
    	ac(x);splay(x);
    	while(lc) down(x),x=lc;
    	splay(x);
    	return x;
    }
    inline void split(cri x,cri y){
    	mrt(x),ac(y),splay(y);
    }
    inline void link(cri x,cri y){
    	if(frt(x)==frt(y)) return;
    	split(x,y);f[x]=y;
    }
    inline void cut(cri x,cri y){
    	split(x,y);
    	if(c[y][0]!=x||c[x][1]) return;
    	f[x]=c[y][0]=0;
    	up(y);
    }
    signed main(){
    	int n,m;
    	scanf("%lld%lld",&n,&m);
    	for(int i=1;i<=n;i++) scanf("%lld",&a[i]),up(i);
    	for(int i=1,x,y;i<n;i++) scanf("%lld%lld",&x,&y),link(x,y);
    	while(m--){
    		int opt,u,x,d;
    		scanf("%lld%lld%lld",&opt,&u,&x);
    		if(opt==1) cut(u,x);
    		if(opt==2) link(u,x);
    		if(opt==3){
    			scanf("%lld",&d);
    			if(frt(u)!=frt(x)) continue;
    			split(u,x);
    			add(x,d);
    		}
    		if(opt==4){
    			if(frt(u)!=frt(x)) puts("-1");
    			else{
    				split(u,x);
    				int ans=la[x]*(sz[x]+1)-l2a[x],base=sz[x]*(sz[x]+1)/2;
    				printf("%lld/%lld
    ",ans/__gcd(ans,base),base/__gcd(ans,base));
    			}
    		}
    	}
    }
    
  • 相关阅读:
    为什么每天都在学习,生活还是没有任何改善?
    MySql基础汇总
    BeanUtils.copyProperties(待复制对象, 待更新对象) || PropertyUtils.copyProperties(待更新对象, 待复制对象)
    ThreadLocal
    synchronized 锁
    STS报could not find tools.jar in the active JRE
    SpringBoot 定时任务 || cron表达式
    lombok注解
    cron表达式
    Thymeleaf 模板引擎
  • 原文地址:https://www.cnblogs.com/mikufun-hzoi-cpp/p/12146406.html
Copyright © 2011-2022 走看看