BZOJ 3779: 重组病毒

本文探讨了一种高效的数据结构——树状数组,用于处理链式赋值问题。通过将点到根路径上的颜色变化转换为链式赋值操作,利用树状数组维护节点代价,实现对节点到根路径上颜色数量的快速查询与更新。文章详细介绍了树状数组的使用方法,包括区间加、区间求和等操作,并提供了具体的代码实现。

神仙题.

题意:
一开始有n个点,每个点有一独特的颜色,根为1,操作如下:

  • 1.把点到根的路径上的点的颜色变成一个新的颜色.
  • 2.把点到根的路径上的点的颜色变成一个新的颜色,并换根.
  • 3.求 x x x子树中的点到根的路径上的颜色的颜色总数期望.

由于1.2操作的缘故,所以同一颜色的只能为一条链.
如果把一条链看成一个 s p l a y splay splay,那么一个点到根路径上的颜色总数就为access()内循环的次数.
我们把每个点的代价看为 a c c e s s ( ) access() access()内循环的次数(即到根的虚边个数+1)
a c c e s s ( ) access() access()的时候,实边变虚边时,子树的所有点的代价+1;虚边变实边时,子树所有点的代价-1.

我们用树状数组维护代价.(其实用线段树更好理解)
(不会用树状数组做区间加、区间求和的请戳这里中的树状数组解法讲解)

为了方便求值,我们先预处理出dfs序。
但需要注意的是,求值要分类讨论:

  1. 当 x = r o o t , q u e r y ( 1 , n ) 当x=root,query(1,n) x=rootquery(1,n)
  2. 当 x 为 r o o t 的 祖 宗 时 , 需 要 剔 除 r o o t 所 属 的 x 的 子 树 . 当x为root的祖宗时,需要剔除root所属的x的子树. xroot,rootx.
  3. 否 则 , q u e r y ( i n [ x ] , o u t [ x ] ) ( 详 见 d f s ( ) ) 否则,query(in[x],out[x])(详见dfs()) ,query(in[x],out[x])(dfs())

modify时类似,只是modify的一定不是root.

详见代码:

#include<cstdio>
#include<cctype>
#include<cstring>
#include<algorithm>
#define g getchar()
#define lc tr[x].son[0]
#define rc tr[x].son[1]
using namespace std;
typedef long long ll;
const int N=1e5+10;
int n,m,fa[N][19],in[N],out[N],ts,dep[N],root,bin[19];
struct edge{int y,next;}a[N<<1];int len,last[N];
void ins(int x,int y){a[++len]=(edge){y,last[x]};last[x]=len;}
struct node{int f,son[2];bool v;}tr[N];
struct option{char op;int x;}Q[N];
namespace bit{
	ll a[N],b[N];//a维护差分数组的前缀和,b维护差分值乘位置的前缀和 
	int lowbit(int x){return x&-x;}
	void up(int x,int d){for(ll y=1LL*x*d;x<=n;x+=lowbit(x))a[x]+=d,b[x]+=y;}//不写1LL会炸 
	void change(int l,int r,int d){up(l,d);up(r+1,-d);}//l~r增加d 
	ll down(int x)
	{
		ll sum(0);
		for(ll y=x+1;x;x-=lowbit(x))sum+=y*a[x]-b[x];
		return sum;
	}
	ll query(int l,int r){return down(r)-down(l-1);}
}
void dfs(int x)
{
	in[x]=++ts;
	bit::change(in[x],in[x],dep[x]);
	for(int k=last[x];k;k=a[k].next)
	{
		int y=a[k].y;
		if(y==fa[x][0])continue;
		tr[y].f=fa[y][0]=x;for(int i=1;fa[y][i-1];i++)fa[y][i]=fa[fa[y][i-1]][i-1];//求祖宗
		dep[y]=dep[x]+1;dfs(y);
	}
	out[x]=ts;
}
int jump(int x,int y)//x单跳y次
{
	for(int i=0;y;i++)if(y&bin[i])y^=bin[i],x=fa[x][i];
	return x;
}
void modify(int x,int d)//把x的关键集合的值,都增加d 
{
	if(!(in[x]<=in[root]&&in[root]<=out[x]))bit::change(in[x],out[x],d);//根不在x的子树内
	else
	{
		x=jump(root,dep[root]-dep[x]-1);//jump(rt,dep[rt]-dep[x]-1)为x的孩子 
		if(in[x]>1) bit::change(1,in[x]-1,d);//逗比了 
		if(out[x]<n)bit::change(out[x]+1,n,d);
	}
}
double ask(int x)
{
	if(x==root)return 1.0*bit::query(1,n)/n;
	else if(!(in[x]<=in[root]&&in[root]<=out[x]))
		return 1.0*bit::query(in[x],out[x])/(out[x]-in[x]+1);
	else
	{
		int y=jump(root,dep[root]-dep[x]-1);
		double s=bit::query(1,in[y]-1);int sz=n-(out[y]-in[y]+1);
		if(out[y]<n)s+=bit::query(out[y]+1,n);
		return s/sz;
	}
}
void fz(int x)
{
	tr[x].v=0;swap(lc,rc);
	tr[lc].v^=1;tr[rc].v^=1;
}
void rotate(int x,int w)
{
	int f=tr[x].f,ff=tr[f].f,r,R;
	r=tr[x].son[w];R=f;tr[R].son[1-w]=r;if(r)tr[r].f=R;
	r=x;R=ff;if(tr[R].son[0]==f)tr[R].son[0]=r;else if(tr[R].son[1]==f)tr[R].son[1]=r;tr[r].f=R;
	r=f;R=x;tr[R].son[w]=r;tr[r].f=R;
}
bool rt(int x){return tr[tr[x].f].son[0]!=x&&tr[tr[x].f].son[1]!=x;}
void wh(int x)
{
	if(!rt(x))wh(tr[x].f);
	if(tr[x].v)fz(x);
}
void splay(int x)
{
	wh(x);
	while(!rt(x))
	{
		int f=tr[x].f;
		if(rt(f))rotate(x,tr[f].son[0]==x);
		else
		{
			int ff=tr[f].f,a=(tr[f].son[0]==x),b=(tr[ff].son[0]==f);
			rotate(a^b?x:f,a);rotate(x,b);
		}
	}
}
int find_root(int x)
{
	if(tr[x].v)fz(x);
	while(lc)
	{
		x=lc;
		if(tr[x].v)fz(x);
	}
	return x;
}
void access(int x)
{
	for(int y=0;x;x=tr[y=x].f)
	{
		splay(x);
		if(rc)modify(find_root(rc),1);
		if(y)modify(find_root(y),-1);
		rc=y;//逗比了 
	}
}
void makeroot(int x)
{
	access(x);splay(x);
	tr[x].v^=1;root=x;
}

void qr(int &x)
{
	char c=g;x=0;
	while(!isdigit(c))c=g;
	while(isdigit(c))x=x*10+c-'0',c=g;
}
int main()
{
	qr(n);qr(m);
	bin[0]=1;for(int i=1;bin[i-1]<=n;i++)bin[i]=bin[i-1]<<1;
	int x,y;char s[11];
	for(int i=1;i<n;i++)
		qr(x),qr(y),ins(x,y),ins(y,x);
	dep[1]=root=1;dfs(1);
	for(int i=1;i<=m;i++)
	{
		scanf("%s",s);qr(Q[i].x);
		switch(s[2]){
			case 'L':Q[i].op=1;break;
			case 'C':Q[i].op=2;break;
			case 'Q':Q[i].op=3;break;
		}
	}
	while(m&&Q[m].op!=3)m--;//后面都是无用功 
	for(int i=1;i<=m;i++)
	{
		x=Q[i].x;
		switch(Q[i].op){
			case 1:access(x);break;
			case 2:makeroot(x);break;
			case 3:printf("%.10lf\n",(ask(x)));break;
		}
	}
	return 0;
}

调试找到的bug:

  1. u p 时 y = 1 L L ∗ x ∗ d 时 未 加 1 L L up时y=1LL * x* d时未加1LL upy=1LLxd1LL
  2. m o d i f y 时 d 写 成 了 1. modify时d写成了1. modifyd1.
  3. a c c e s s 时 忘 记 写 r c = y ; access时忘记写rc=y; accessrc=y;
内容概要:本文介绍了一种基于双层优化的微电网系统规划设计方法,旨在通过Matlab代码实现,解决微电网在规划与运行中的多目标、多层次决策问题。该方法将优化过程分为上下两层:上层通常负责容量配置、设备选址等长期规划决策,下层则聚焦于能量管理、出力调度等短期运行优化,通过迭代交互实现全局最优。文中详细阐述了模型构建、约束条件设定、目标函数设计及求解算法实现流程,并提供了完整的Matlab代码供复现实验,有助于深入理解微电网系统的设计逻辑与优化机制。; 适合人群:具备一定电力系统基础知识和Matlab编程能力,从事新能源、微电网、综合能源系统等领域研究的研究生、科研人员及工程技术人员。; 使用场景及目标:① 学习和掌握双层优化理论在微电网规划设计中的具体应用;② 通过阅读和运行Matlab代码,复现并改进经典优化模型,用于学位论文、科研项目或实际工程方案设计;③ 深入理解微电网中分布式能源、储能与负荷的协同优化调度策略。; 阅读建议:此资源以Matlab代码实现为核心,强调理论与实践的结合。建议读者先理解双层优化的基本思想和数学模型,再结合代码逐行分析,重点关注变量定义、约束条件的代码转化以及主从问题间的迭代逻辑。鼓励在提供的代码基础上进行参数调整、场景扩展或算法改进,以深化学习效果。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Infinite_Jerry

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值