[Codeforces1046D][BFS序][树状数组]Interstellar battle

翻译

给出一棵树以及树上的点消失的概率
Q次修改
每次修改一个点消失的概率
问每次修改之后当前树剩下的连通块个数的期望
Q<=200000 N<=100000

题解

通过这题gay到了一个新姿势:
树上连通块个数=点数-边数
相当于动态维护一个 E ( V − E ) E(V-E) E(VE)
根据期望的线性性,上式可以写为 E ( V ) − E ( E ) E(V)-E(E) E(V)E(E)
可以先将消失的概率转化为存在的概率 记为 c a l [ i ] cal[i] cal[i]
显然 E ( V ) = ∑ c a l [ i ] E(V)=\sum cal[i] E(V)=cal[i]
下来考虑动态维护 E ( E ) E(E) E(E)
我们考虑一条边存在的期望 当且仅当它所连接的两个点均没消失 记这两点为 ( u , v ) (u,v) (u,v)
则一条边 K K K存在的期望为 E ( u ) ∗ E ( v ) E(u)*E(v) E(u)E(v)
相当于维护一个 ∑ E ( u ) ∗ E ( v ) \sum E(u)*E(v) E(u)E(v)
考虑一个点被修改的影响
只会影响与他相连的边
这些边可以分为两类 一类连向儿子一类连向父亲虽然父亲只有一条边
父亲这条边的贡献可以暴力改掉
搞出BFS序 显然一个点的所有儿子的BFS序是连续的
树状数组乱撸

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<cmath>
#include<queue>
#include<vector>
#define LL long long
#define mp(x,y) make_pair(x,y)
using namespace std;
inline int read()
{
	int f=1,x=0;char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
	while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
	return x*f;
}
inline void write(int x)
{
	if(x<0)putchar('-'),x=-x;
	if(x>9)write(x/10);
	putchar(x%10+'0');
}
inline void print(int x){write(x);printf(" ");}
struct node{int x,y,next;}a[210000];int len,last[110000];
void ins(int x,int y){len++;a[len].x=x;a[len].y=y;a[len].next=last[x];last[x]=len;}
queue<int> li;
int fa[110000];
void pre_tree_node(int x)
{
	for(int k=last[x];k;k=a[k].next)
		if(a[k].y!=fa[x])fa[a[k].y]=x,pre_tree_node(a[k].y);
}
int in[110000],ot[110000],deg[110000],bfn;
int fir[110000];
void init_BFS()
{
	li.push(1);in[1]=++bfn;
	while(!li.empty())
	{
		int x=li.front();fir[x]=bfn;
		for(int k=last[x];k;k=a[k].next)
			if(a[k].y!=fa[x])
			{
				int y=a[k].y;
				in[y]=++bfn;
				li.push(y);	
			}
		li.pop();ot[x]=bfn;
	}
}
double s[110000];
int n,T;
int lowbit(int x){return x&-x;}
void change(int x,double c){for(int i=x;i<=n;i+=lowbit(i))s[i]+=c;}
double findsum(int x){double ret=0;for(int i=x;i>=1;i-=lowbit(i))ret+=s[i];return ret;}
double cal[110000];
int main()
{
	n=read();
	for(int i=1;i<=n;i++)scanf("%lf",&cal[i]),cal[i]=1.0-cal[i];
	for(int i=1;i<n;i++)
	{
		int x=read(),y=read();
		x++;y++;
		ins(x,y);ins(y,x);
	}
	pre_tree_node(1);
	init_BFS();
	for(int i=1;i<=n;i++)change(in[i],cal[i]);
	T=read();
	double s1=0,s2=0;
	for(int i=1;i<=n;i++)s1+=cal[i];
	for(int i=1;i<=n;i++)
	{
		double S=findsum(ot[i])-findsum(fir[i]);
		s2+=cal[i]*S;
	}
	//printf("%.5lf\n",s1-s2);
	while(T--)
	{
		int x=read();
		double c;scanf("%lf",&c);
		c=1-c;x++;
		s1-=cal[x];s1+=c;
		double S=findsum(ot[x])-findsum(fir[x]);
		s2-=cal[x]*S;s2+=c*S;
		s2-=cal[x]*cal[fa[x]];s2+=c*cal[fa[x]];
		change(in[x],-cal[x]+c);
		cal[x]=c;
		printf("%.5lf\n",s1-s2);
	}
	return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值