您的位置:首页 > 理论基础 > 数据结构算法

数据结构day2 lca

2018-02-06 21:57 204 查看
丁叔叔的恐怖红包

小庆住在一个很特别的国度里,它有N 个城市,并且只建了N ?1 条双向路,但神奇的是任意两个城市

都可以通过这些路连接起来。小庆最近在研究寒假的旅游计划,有时她想快速地知道两个城市之间的距

离,于是找你来帮帮解决。

Input

第1 行一个整数N。

接下来N ? 1 行,每行三个整数u; v;w,表示城市u 和城市v 之间有一条长为w 的路。

接下来1 行,包含一个整数Q,表示小庆有Q 个询问。

接下来Q 行,每行两个整数u; v,表示小庆想知道u 和v 这两个城市之间的距离。

Output

对于小庆的每个询问,输出两个城市之间的距离。

#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxn=200005;
const int p=20;
int wi[maxn],dep[maxn],dis[maxn],pos[maxn],nxt[maxn],tov[maxn],head[maxn],anc[maxn][p+1];
int q,n,tot=0,root;
void add(int i,int j,int w)
{
tot++;
tov[tot]=j;
nxt[tot]=head[i];
wi[tot]=w;
head[i]=tot;
}
void dfs(int u,int f)
{
anc[u][0]=f;
for (int i=1;i<=p;i++)
anc[u][i]=anc[anc[u][i-1]][i-1];
for (int i=head[u];i;i=nxt[i])
{
int v=tov[i];
if (v==f)
continue;
dep[v]=dep[u]+1;
dis[v]=dis[u]+wi[i];
dfs(v,u);
}
}
int lca(int u,int v)
{
if (dep[u]<dep[v])
swap(u,v);
int t=dep[u]-dep[v];
for (int i=0;t;t>>=1,i++){
if (t&1)
u=anc[u][i];
}if (u==v)
return u;//惨痛教训
for (int i=p;anc[u][0]!=anc[v][0];i--)
if (anc[u][i]!=anc[v][i])
{
u=anc[u][i];
v=anc[v][i];
}
return anc[u][0];
}
int main()
{
freopen("distance.in","r",stdin);
freopen("distance.out","w",stdout);
scanf("%d",&n);
for (int i=1;i<=n-1;i++)
{
int u,v,w;
scanf("%d%d%d",&u,&v,&w);
add(u,v,w);
add(v,u,w);
}
dep[1]=1;
dis[1]=0;
dfs(1,1);
scanf("%d",&q);
for (int i=1;i<=q;i++)
{
int x,y,ans=0;
scanf("%d%d",&x,&y);
int lca1=lca(x,y);
printf("%d\n",dis[x]+dis[y]-2*dis[lca1]);
}
return 0;
}
/*
4
1 2 3
1 3 4
2 4 2
3
1 1
1 4
2 3
*/(承上题)小漫是小庆那个国家的国王,她住在1 号城市,u 号城市如果到1 必定经过v 号城市,我们则

称v 号城市管辖u 号城市(v 号城市也管辖自己)。过年了,小漫想给国家的一些城市发红包,每次她会

给u 号城市管辖的每个城市发放w 的红包,有时,她也想知道某个城市或被某个城市管辖的城市一共得

了多少红包。如下:

• give u w :表示将u 号城市管辖的每个城市发w 的红包。

• single u :表示询问u 号城市得了多少红包。

• all u :表示询问u 号城市管辖的城市一共得了多少红包。

Input

第1 行一个整数N。

接下来N ? 1 行,每行三个整数u; v,表示城市u 和城市v 之间有一条路。

接下来1 行,包含一个整数Q,表示小漫有Q 个操作。

接下来Q 行,每行是上面三种操作的一种。

Output

对每个询问,输出其答案。
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;

const int N=200005;

int flag[N*2],out
,in
,tot,dd,tov
,head
,nex
;
long long f[N*2],tag[N*2];
char s1[15];
void add(int x,int y)
{
tot++;
tov[tot]=y;
nex[tot]=head[x];
head[x]=tot;
}
void dfs(int u,int f)
{
dd++;
in[u]=dd;
for(int i=head[u];i;i=nex[i])
{
int v=tov[i];
if(v==f) continue;
dfs(v,u);
}
out[u]=dd;
}
void update(int o)
{
f[o]=f[o*2]+f[o*2+1];
}
void push_down(int o,int l,int r)
{
int mid=(l+r)/2;
if(flag[o]==1)
{
f[o*2]+=(mid-l+1)*tag[o];
f[o*2+1]+=(r-mid)*tag[o];
tag[o*2]+=tag[o];
tag[o*2+1]+=tag[o];
flag[o*2]=1;
flag[o*2+1]=1;
tag[o]=flag[o]=0;
}
}
long long query(int o,int l,int r,const int L,const int R)
{
if(L<=l&&R>=r)
return f[o];
push_down(o,l,r);
int mid=(r+l)/2;
long long ans=0;
if(L<=mid) ans+=query(o*2,l,mid,L,R);
if(R>mid) ans+=query(o*2+1,mid+1,r,L,R);
return ans;
}
void change(int o,int l,int r,const int L,const int R,long long data)
{
if(L<=l&&R>=r)
{
f[o]+=data*(r-l+1);
tag[o]+=data;
flag[o]=1;
return;
}
push_down(o,l,r);
int mid=(l+r)/2;
if(L<=mid) change(o*2,l,mid,L,R,data);
if(R>mid) change(o*2+1,mid+1,r,L,R,data);
update(o);
}
int main()
{
freopen("redpacket.in","r",stdin);
freopen("redpacket.out","w",stdout);
int m,n,y,x;
scanf("%d",&n);
for(int i=1;i<n;i++)
{
scanf("%d%d",&x,&y);
add(x,y);
add(y,x);
}
scanf("%d",&m);
dfs(1,1);
for(int i=1;i<=m;i++)
{
scanf("%s",s1);
if(s1[0]=='g')
{
scanf("%d%d",&x,&y);
change(1,1,n,in[x],out[x],y);
}
if(s1[0]=='s')
{
scanf("%d",&x);
long long la=query(1,1,n,in[x],out[x]);
if(in[x]==out[x])
{
printf("%I64d\n",la);
}
else
{
long long ans;
long long la2=query(1,1,n,in[x]+1,out[x]);
ans=la-la2;
printf("%I64d\n",ans);
}
}
if(s1[0]=='a')
{
scanf("%d",&x);
long long la=query(1,1,n,in[x],out[x]);
printf("%I64d\n",la);
}
}
return 0;
}
/*
5
1 2
1 3
2 4
2 5
3
give 1 5
single 2
all 2
*/(承上题)小漫觉得那样发红包有些无聊,于是决定每次给u 号城市到v 号城市简单路径经过的城市发

红包(当然包括u 和v 本身),考虑到你们还太年轻,就只问你们单个城市的红包了:

• give u v w :给u 到v 的简单路径上的城市w 的红包

• single u :询问到目前为止u 号城市得到的红包

Input

第1 行一个整数N。

接下来N ? 1 行,每行三个整数u; v,表示城市u 和城市v 之间有一条路。

接下来1 行,包含一个整数Q,表示小漫有Q 个操作。

接下来Q 行,每行是上面两个操作中的一个。

Output

对于每个询问,输出其答案。

跑一波std指针
#include <cstdio>
#include <algorithm>
using namespace std;

const int N = 100010;
const int M = N + N;
const int P = 16;

struct Node {
long long sum;
Node *ls, *rs;
void update() {
sum = ls->sum + rs->sum;
}
}pool[N*3], *tail=pool, *root;

int n, q;
int head
, dest[M], last[M], etot;
int in
, out
, seq
, dep
, anc
[P+1], idc;

void adde( int u, int v ) {
etot++;
dest[etot] = v;
last[etot] = head[u];
head[u] = etot;
}
void dfs( int u, int f ) {
seq[++idc] = u;
in[u] = idc;
anc[u][0] = f;
for( int p = 1; p <= P; p++ )
anc[u][p] = anc[anc[u][p-1]][p-1];
for( int t = head[u]; t; t = last[t] ) {
int v = dest[t];
if( v == f ) continue;
dep[v] = dep[u] + 1; // idy!!!!!!!!! I'm angry!!!
dfs( v, u );
}
out[u] = idc;
}
int lca( int u, int v ) {
if( dep[u] < dep[v] ) swap(u,v);
int t = dep[u] - dep[v];
for( int p = 0; t; t>>=1, p++ )
if( t & 1 ) u = anc[u][p];
if( u == v ) return u;
for( int p = P; anc[u][0] != anc[v][0]; p-- )
if( anc[u][p] != anc[v][p] )
u = anc[u][p], v = anc[v][p];
return anc[u][0];
}
Node *build( int lf, int rg ) {
Node *nd = ++tail;
if( lf == rg ) {
nd->sum = 0;
} else {
int mid = (lf + rg) >> 1;
nd->ls = build( lf, mid );
nd->rs = build( mid+1, rg );
nd->sum = 0;
}
return nd;
}
long long query( Node *nd, int lf, int rg, int L, int R ) {
if( L <= lf && rg <= R )
return nd->sum;
int mid = (lf + rg) >> 1;
long long rt = 0;
if( L <= mid )
rt += query( nd->ls, lf, mid, L, R );
if( R > mid )
rt += query( nd->rs, mid + 1, rg, L, R );
return rt;
}
void modify( Node *nd, int lf, int rg, int pos, int delta ) {
if( lf == rg ) {
nd->sum += delta;
return;
}
int mid = (lf + rg)>>1;
if( pos <= mid )
modify( nd->ls, lf, mid, pos, delta );
else
modify( nd->rs, mid+1, rg, pos, delta );
nd->update();
}
int main() {
freopen ( "redpacket2.in", "r", stdin ) ;
freopen ( "redpacket2.out", "w", stdout ) ;
scanf( "%d", &n );
for( int i = 1; i < n; i++ ) {
int u, v;
scanf( "%d%d", &u, &v );
adde( u, v );
adde( v, u );
}
idc = 0;
dep[1] = 1;
dfs( 1, 1 );
root = build( 1, idc );
scanf( "%d", &q );
while( q-- ) {
char ss[100];
int u, v, w;
scanf( "%s", ss );
if( ss[0] == 'g' ) {
scanf( "%d%d%d", &u, &v, &w );
int ca = lca(u,v);
modify( root, 1, idc, in[u], w );
modify( root, 1, idc, in[v], w );
modify( root, 1, idc, in[ca], -w );
if( ca != 1 )
modify( root, 1, idc, in[anc[ca][0]], -w );
} else {
scanf( "%d", &u );
printf( "%lld\n", query(root,1,idc,in[u],out[u]) );
}
}
}
lca版子记错爆0异常难受
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: