数据结构——树
一些基础知识:
1.二叉搜索树:又名有序二叉树,结点元素按固定次序排布,使得我们可以在进行查找等操作时使用二分搜索提高效率。
它最明显的特征是: 父结点值大于左子树任意结点值,小于右子树任意结点值。
2.树的重心也叫树的质心。对于一棵树n个节点的树,找到一个点,使得把树变成以该点为根。
重心的三个的性质:
- 树中所有点到某个点的距离和中,到重心的距离和是最小的;如果有两个重心,那么他们的距离和一样。
- 把两个树通过一条边相连得到一个新的树,那么新的树的重心在连接原来两个树的重心的路径上。
- 把一个树添加或删除一个叶子,那么它的重心最多只移动一条边的距离。
树的重心模板代码:
//***************树的重心
#include<bits/stdc++.h>
#define mp make_pair
#define se second
#define fi first
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
typedef pair<ll, int> pli;
typedef pair<ll, ll> pll;
typedef long double ld;
const int N=2e4+10;
const int maxn=20010;
const int INF=0x3f3f3f3f;
const double eps=0.0000001;
const ll mod=998244353;
int n,m,x,y,z,k,cnt,t,len,q;
vector<int>G[maxn];
int son[maxn];//然后设son[i]表示以i为根的子树的节点个数(不包括根)
int ans,balance;//ans重心 balance平衡最大子树的最小节点
void ReadTree()
{//输入n-1条边
scanf("%d",&n);
for(int i=0;i<n-1;i++)
{
int s,e;
scanf("%d%d",&s,&e);
G[s].push_back(e);
G[e].push_back(s);
}
}
void dfs(int u,int fa)
{//O(n)搜一遍即可
son[u]=0;
int d=G[u].size();
int pre_balance=0;
for(int i=0;i<d;i++)
{
int v=G[u][i];
if(v!=fa)
{
dfs(v,u);
son[u]+=son[v]+1;//合并子树
pre_balance=max(pre_balance,son[v]+1);
//如果以u为重心 算平衡子叶
}
}
//如果以u的父亲为重心 算平衡子叶
pre_balance=max(pre_balance,n-son[u]-1);
//printf("删除节点: %d 最大子树节点数%d\n",v,pre_balance);
if(pre_balance<balance||(pre_balance==balance&&u<ans))
{
ans=u;
balance=pre_balance;
//printf("ans=%d bal=%d\n",ans,balance);
}
}
//scanf("%lld%lld",&n,&m);
//printf("%lld\n",ans);
//for(int i=1;i<=n;i++)
int main()
{
ReadTree();
memset(son,0,sizeof(son));
ans=0;balance=INF;
dfs(1,-1);
printf("重心%d\n平衡%d",ans,balance);
return 0;
}
3.树的直径是指树的最长简单路,即树的最长简单路/最远点对。 树的中心是树的直径的中点,或某一段链
如何求解树的直径——现有结论:从任意一点
u
u
u出发搜到的最远的点一定是
s
、
t
s、t
s、t中的一点,然后在从这个最远点开始搜,就可以搜到另一个最长路的端点,即用两遍广搜就可以找出树的最长路。
step1:以树中任意一个结点为源点,进行一次广度优先遍历,找出离源点距离最远的点
d
d
d
step2:以
d
d
d为源点,进行一次广度优先遍历,找出离
d
d
d最远的点,并记录其长度
#include <bits/stdc++.h>
using namespace std;
const int N = 10010;
typedef pair<int, int> P; // first是下一个点,second是从当前点到下一个点的距离
int dis[N]; // 保存从起点到其他点的距离
int used[N]; // 标记点是否走过
int ans = 0;
vector<P> G[N]; // 邻接表存图(树)
int bfs(int x)
{
memset(dis, 0, sizeof(dis));
memset(used, 0, sizeof(used));
queue<int> que;
que.push(x);
used[x] = 1;
int poi = 0;
while(!que.empty())
{
int v = que.front(); que.pop();
if(dis[v] > ans) //
{
ans = dis[v];
poi = v;
}
P p;
for(int i=0; i<G[v].size(); i++)
{
p = G[v][i];
if(used[p.first] == 0)
{
used[p.first] = 1;
dis[p.first] = dis[v] + p.second;
que.push(p.first);
}
}
}
return poi;
}
int main()
{
int a, b, c;
char ch;
while(scanf("%d %d %d", &a, &b, &c) != EOF)
{
G[a].push_back(P(b, c)); // 两种写法等效
G[b].push_back(make_pair(a, c));
}
ans = 0;
int point = bfs(1);
ans = 0;
bfs(point);
printf("%d\n", ans);
return 0;
}
结论:树的直径中点到各个点的最大距离最小。
证明:设树的直径的中心到两端的距离为
s
1
,
s
2
s1,s2
s1,s2其中
s
1
s1
s1大于等于
s
2
s2
s2,一定不存在一个点(除了直径端点)到这个中点的距离大于
s
2
s2
s2,因为如果存在,就会出现一条新的直径
s
1
+
s
3
>
s
1
+
s
2
s1+s3 >s1+s2
s1+s3>s1+s2与我们已知的直径是
s
1
+
s
2
s1+s2
s1+s2相互违背。 也就是说树直径的中点到各个点的距离最大值最小。
线段树的应用:
线段数详解
修改:统一加上一个数,或者重置为一个值
查询:区间和,最大值,最小值,其他
#include<bits/stdc++.h>
using namespace std;
const int N=1e6+5;
int a[N];
int lazy[N*4];
int tree[N*4];
void push_up(int rt)
{
tree[rt]=tree[rt<<1]+tree[rt<<1|1];
}
void push_down(int rt,int len)
{
lazy[rt<<1]+=lazy[rt];
lazy[rt<<1|1]+=lazy[rt];
tree[rt<<1]+=(len-(len>>1))*lazy[rt];
tree[rt<<1|1]+=(len>>1)*lazy[rt];
lazy[rt]=0;
}
void build(int rt,int l,int r)
{
if(l == r) {
scanf("%d", &a[l]);
tree[rt] = a[l];
return ;
}
int mid=(l+r)>>1;
build(rt << 1,l,mid);
build(rt << 1|1,mid+1,r);
push_up(rt);
}
void update(int p,int x,int rt,int l,int r)
{
if(l==r){
tree[rt]+=x;
return;
}
int m=l+r >> 1;
if(p<=m) update(p,x,rt<<1,l,m);
else update(p,x,rt<<1|1,m+1,r);
push_up(rt);
}
void Update(int L,int R,int x,int rt,int l,int r)
{
if(L<=l&&r<=R){
tree[rt]+=(r-l+1)*x;
lazy[rt]+=x;
return;
}
if(lazy[rt]) push_down(rt,r-l+1);
int m=l+r>>1;
if(L<=m) Update(L,R,x,rt<<1,l,m);
if(R>m) Update(L,R,x,rt<<1|1,m+1,r);
push_up(rt);
}
int query(int L,int R,int rt,int l,int r)
{
if(L<=l&&r<=R) return tree[rt];
int m=l+r >>1;
int ans=0;
if(lazy[rt]) push_down(rt,r-l+1);
if(L<=m) ans+=query(L,R,rt<<1,l,m);
if(R>m) ans+=query(L,R,rt<<1|1,m+1,r);
return ans;
}
int main()
{
int n,m,ty,p,x,L,R;
scanf("%d",&n);
build(1,1,n);
scanf("%d",&m);
for(int i=1;i<=m;i++)
{
scanf("%d",&ty);
if(ty==1)
{
scanf("%d%d%d",&L,&R,&x);
Update(L,R,x,1,1,n);
}
else
{
scanf("%d%d",&L,&R);
printf("%d\n",query(L,R,1,1,n));
}
}
//cout << "Hello world!" << endl;
return 0;
}