Housewife Wind POJ - 2763 倍增LCA+树状数组 或 树链剖分+线段树

博客围绕一个树的问题展开,包含q个询问,起点为s,有查询两点距离和修改边权两种询问。介绍了两种解法,解法一用时间戳代替节点,结合差分区间和树状数组及lca求最短路;解法二是树链剖分模板题,可解决树上区间修改和查询问题。

     题目 链接:http://poj.org/problem?id=2763

Language:Default

Housewife Wind

Time Limit: 4000MS Memory Limit: 65536K
Total Submissions: 16100 Accepted: 4388

Description

After their royal wedding, Jiajia and Wind hid away in XX Village, to enjoy their ordinary happy life. People in XX Village lived in beautiful huts. There are some pairs of huts connected by bidirectional roads. We say that huts in the same pair directly connected. XX Village is so special that we can reach any other huts starting from an arbitrary hut. If each road cannot be walked along twice, then the route between every pair is unique. 

Since Jiajia earned enough money, Wind became a housewife. Their children loved to go to other kids, then make a simple call to Wind: 'Mummy, take me home!' 

At different times, the time needed to walk along a road may be different. For example, Wind takes 5 minutes on a road normally, but may take 10 minutes if there is a lovely little dog to play with, or take 3 minutes if there is some unknown strange smell surrounding the road. 

Wind loves her children, so she would like to tell her children the exact time she will spend on the roads. Can you help her? 

Input

The first line contains three integers n, q, s. There are n huts in XX Village, q messages to process, and Wind is currently in hut s. n < 100001 , q < 100001. 

The following n-1 lines each contains three integers a, b and w. That means there is a road directly connecting hut a and b, time required is w. 1<=w<= 10000. 

The following q lines each is one of the following two types: 

Message A: 0 u 
A kid in hut u calls Wind. She should go to hut u from her current position. 
Message B: 1 i w 
The time required for i-th road is changed to w. Note that the time change will not happen when Wind is on her way. The changed can only happen when Wind is staying somewhere, waiting to take the next kid. 

Output

For each message A, print an integer X, the time required to take the next child.

Sample Input

3 3 1
1 2 1
2 3 2
0 2
1 2 3
0 3

Sample Output

1
3

Source

POJ Monthly--2006.02.26,zgl & twb

 

题意:给出一个树,q个询问,s为起点,0 u 询问由目前所在点到u点的距离,1 k val代表修改第k条边的权值为val

解法一:

初学LCA,刚入门的第一题,题目lca+树状数组的思想还是比较好的,记录一下

ps:poj此题G++超时,C++ AC 可能卡STL,建议换成链向星

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
using namespace std;
const int maxn=2e5+100;
struct node
{
	int to,val,id;
	node(int a,int b,int c):to(a),val(b),id(c){}
};
vector<node> v[maxn];
int fa[maxn][20],dis[maxn],depth[maxn];
int c[maxn],id[maxn],l[maxn],r[maxn],num,cc[maxn];
int n,s,q;
//lca部分 
void dfs(int u)
{
	l[u]=++num;//dfs序 将树形结构转换成线性结构
	for(int i=0;i<v[u].size();i++)
	{
		node te=v[u][i];
		if(te.to==fa[u][0]) continue;
		fa[te.to][0]=u;
		dis[te.to]=dis[u]+te.val;
		depth[te.to]=depth[u]+1;
		id[te.id]=te.to;//id[i] 代表第i条边对应的离根节点远的节点
		dfs(te.to);
	}
	r[u]=num;
}
void init()
{
	for(int j=1;(1<<j)<=n;j++)
	{
		for(int i=1;i<=n;i++)
			fa[i][j]=fa[fa[i][j-1]][j-1];
	}
}
int lca(int a,int b)
{
	if(depth[a]>depth[b]) swap(a,b);
	int f=depth[b]-depth[a];
	for(int i=0;(1<<i)<=f;i++)
	{
		if((1<<i)&f) b=fa[b][i];
	}
	if(a==b) return a;
	for(int i=19;i>=0;i--)
	{
		if(fa[a][i]!=fa[b][i])
		{
			a=fa[a][i];
			b=fa[b][i];
		}
	}
	return fa[a][0];
}
//树状数组部分 
int lowerbit(int x)
{
	return x&(-x);
} 
void add(int pos,int x)
{
	while(pos<=n)
	{
		cc[pos]+=x;
		pos+=lowerbit(pos);
	}
}
int sum(int pos)
{
	int ans=0;
	while(pos>0)
	{
		ans+=cc[pos];
		pos-=lowerbit(pos);
	 } 
	 return ans;
}

int main()
{
	while(~scanf("%d%d%d",&n,&q,&s))
	{
		memset(v,0,sizeof(v));
		memset(c,0,sizeof(c));
		memset(cc,0,sizeof(cc));
		num=0;
		for(int i=1;i<n;i++)
		{
			int a,b,cc;
			scanf("%d%d%d",&a,&b,&cc);
			v[a].push_back(node(b,cc,i));
			v[b].push_back(node(a,cc,i));
			c[i]=cc;
		}
		fa[1][0]=0,dis[1]=0,depth[1]=0;
		dfs(1);
		init();
		for(int i=1;i<n;i++)//对图中原有的节点进行加边操作 
			add(l[id[i]],c[i]),add(r[id[i]]+1,-c[i]);
		while(q--)
		{  
			int op;
			scanf("%d",&op);
			if(op==0)
			{
				int u;
				scanf("%d",&u);
				//用dfs序代替节点 使得节点出现顺序 改变之前节点以后节点相应受到影响 
				printf("%d\n",sum(l[u])+sum(l[s])-2*sum(l[lca(s,u)])); 		
				s=u;                                                 
			}
			else if(op==1)
			{
				int k,va;
				scanf("%d%d",&k,&va);
				int tmp=c[k];
				c[k]=va;
				add(l[id[k]], va-tmp); add(r[id[k]]+1, tmp-va);//差分区间简单运用
			}
		}
	}
	return 0;
 } 

题意没有太多坑,自己跑下样例应该就能理解了

给组样例:

5 5 1
1 2 3
2 3 4
1 4 2
2 5 1
0 3
1 4 3
1 1 4
0 4
0 5

结果:

7
10
9

总结:题目利用时间戳来代替节点,这样每个节点在树中遍历的顺序体现出来,如果改变v u两节点的对应边的权值,那么到以u为根的子节点长度(根节点到子节点长度)均受到改变(包括u,默认u离根节点远),因此对l[i] 加减 对r[i] 相反操作可以只影响到以u为根节点的子节点的权值,通过求前缀和来计算根节点到此节点的长度(差分区间简单运用,点修改,区间查询用树状数组),再通过lca可以简单求出两点的最短路了

解法二:

树链剖分的模板题,边权的单点修改,以及区间查询

见代码注释:

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
using namespace std;
const int maxn=2e5+100;
int fa[maxn],top[maxn],depth[maxn],l[maxn],size[maxn],son[maxn];
int tt;
int sum[maxn<<2];
vector<int> v[maxn];
int n,q,s;
int cnt, head[maxn];
struct Edge {
    int to, next;
}edge[maxn];
void add_edge(int f,int t)
{
	edge[cnt].to=t;
	edge[cnt].next=head[f];
	head[f]=cnt++;
}
//预处理部分 
void dfs1(int u,int pre)
{
	size[u]=1;
	for(int i = head[u]; ~i; i= edge[i].next) 
	{
		int te=edge[i].to;
		if(te==pre ) continue;
		depth[te]=depth[u]+1;
		dfs1(te,u);
		fa[te]=u;
		size[u]+=size[te]; 
		if(son[u]==0||size[te]>size[son[u]])
			son[u]=te;
	}
}
void dfs2(int u,int ft)
{
	l[u]=++tt;
	top[u]=ft;
	if(son[u]) dfs2(son[u],top[u]);
	for(int i = head[u]; ~i; i = edge[i].next) {
		int te=edge[i].to;
		if(te==fa[u]||te==son[u]) continue;
		dfs2(te,te);
	}
}
struct node
{
	int f,t,val;
}e[maxn];
//线段树部分 
void update(int p,int val,int l,int r,int rt)
{
	if(l==r){
		sum[rt]=val;
		return ;
	}
	int m=(l+r)>>1;
	if(p<=m) update(p,val,l,m,rt<<1);
	else update(p,val,m+1,r,rt<<1|1);
	sum[rt]=sum[rt<<1]+sum[rt<<1|1]; 
}
int query(int L,int R,int l,int r,int rt)
{
	if(L<=l&&r<=R)
	{
		return sum[rt];
	}
	int m=(l+r)>>1;
	int ans=0;
	if(L<=m) ans+=query(L,R,l,m,rt<<1);
	if(R>m) ans+=query(L,R,m+1,r,rt<<1|1);
	return ans; 
}
//熟练剖分核心 
void change(int k,int val){
	int f=e[k].f,t=e[k].t;
	if(depth[f]>depth[t]) swap(f,t);//单点更新 
	update(l[t],val,1,n,1);
}
int get(int x,int y)
{
	int fx=top[x],fy=top[y];
	int ans=0;
	while(fx!=fy){
		if(depth[fx]>depth[fy]){
			swap(fx,fy);
			swap(x,y);
		}
		ans+=query(l[fy],l[y],1,n,1);
		y=fa[fy];
		fy=top[y];
	}
	if(x==y) return ans;
	if(depth[x]>depth[y]) swap(x,y);
	ans+=query(l[son[x]],l[y],1,n,1);//边权 选取x的重儿子 因为l[x]代表x节点和其父节点的变 直接用x会包括与父节点的边 
	return ans;
}
int main()
{
	scanf("%d%d%d",&n,&q,&s);
	memset(head,-1,sizeof(head));
	cnt=0;
	tt=0;
	for(int i=1;i<n;i++)
	{
		int f,t,val;
		scanf("%d%d%d",&f,&t,&val);
		e[i].f=f;
		e[i].t=t;
		e[i].val=val;
		add_edge(f,t);
		add_edge(t,f);
	}
	fa[1]=0;
	depth[1]=0;
	dfs1(1,0);
	dfs2(1,1);
	for(int i=1;i<n;i++)
	{
		int f=e[i].f,t=e[i].t;
		int val=e[i].val;
		if(depth[f]>depth[t]) swap(f,t);
		update(l[t],val,1,n,1);
	}
	while(q--)
	{
		int op;
		scanf("%d",&op);
		if(op==0){
			int t;
			scanf("%d",&t);
			printf("%d\n",get(s,t)); 
			s=t;
		}
		else
		{
			int k,val;
			scanf("%d%d",&k,&val);
			change(k,val); 
		}
	}
	return 0;
 } 

一点思考:树链剖分很好的解决了树上的区间修改和区间查询问题,本题单点修改用lca+BIT也可以很好的解决

注意:poj卡vector

基于数据驱动的 Koopman 算子的递归神经网络模型线性化,用于纳米定位系统的预测控制研究(Matlab代码实现)内容概要:本文围绕“基于数据驱动的Koopman算子的递归神经网络模型线性化”展开,旨在研究纳米定位系统的预测控制问题,并提供完整的Matlab代码实现。文章结合数据驱动方法与Koopman算子理论,利用递归神经网络(RNN)对非线性系统进行建模与线性化处理,从而提升纳米级定位系统的精度与动态响应性能。该方法通过提取系统隐含动态特征,构建近似线性模型,便于后续模型预测控制(MPC)的设计与优化,适用于高精度自动化控制场景。文中还展示了相关实验验证与仿真结果,证明了该方法的有效性和先进性。; 适合人群:具备一定控制理论基础和Matlab编程能力,从事精密控制、智能制造、自动化相关领域研究的研究生、科研人员及工程技术人员。; 使用场景及目标:①应用于纳米级精密定位系统(如原子力显微镜、半导体制造设备)中的高性能控制设计;②为非线性系统建模与线性化提供一种结合深度学习与现代控制理论的新思路;③帮助读者掌握Koopman算子、RNN建模与模型预测控制的综合应用。; 阅读建议:建议读者结合提供的Matlab代码逐段理解算法实现流程,重点关注数据预处理、RNN结构设计、Koopman观测矩阵构建及MPC控制器集成等关键环节,并可通过更换实际系统数据进行迁移验证,深化对方法泛化能力的理解。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值