暑假训练DAY29(LCA与树链剖分)

How far away ?

 HDU - 2586 

There are n houses in the village and some bidirectional roads connecting them. Every day peole always like to ask like this "How far is it if I want to go from house A to house B"? Usually it hard to answer. But luckily int this village the answer is always unique, since the roads are built in the way that there is a unique simple path("simple" means you can't visit a place twice) between every two houses. Yout task is to answer all these curious people.

Input

First line is a single integer T(T<=10), indicating the number of test cases. 
  For each test case,in the first line there are two numbers n(2<=n<=40000) and m (1<=m<=200),the number of houses and the number of queries. The following n-1 lines each consisting three numbers i,j,k, separated bu a single space, meaning that there is a road connecting house i and house j,with length k(0<k<=40000).The houses are labeled from 1 to n. 
  Next m lines each has distinct integers i and j, you areato answer the distance between house i and house j.

Output

For each test case,output m lines. Each line represents the answer of the query. Output a bland line after each test case.

Sample Input

2
3 2
1 2 10
3 1 15
1 2
2 3

2 2
1 2 100
1 2
2 1

Sample Output

10
25
100
100

LCA的裸模板题,求出两点的LCA之后用两点到根节点的距离减去LCA到根节点的距离的两倍即可

#include<iostream>
#include<cstdio>
#include<vector>
using namespace std;
const int size=4e4+5;
struct node{
	int u,v,w;
	node(){}
	node(int u,int v,int w):u(u),v(v),w(w){} 
};
vector<node> G[size];
int dis[size],dep[size],pa[size][20];
void dfs(int u,int F,int d,int dd)
{
	dep[u]=d;
	dis[u]=dd;
	if(u==1) 
	{
		for(int i=0;i<20;i++) pa[u][i]=1;
	}
	else
	{
		pa[u][0]=F;
		for(int i=1;i<20;i++)
		{
			pa[u][i]=pa[pa[u][i-1]][i-1];
		}
	}
	for(int i=0;i<G[u].size();i++)
	{
		int v=G[u][i].v,c=G[u][i].w;
		if(v==F) continue;
		dfs(v,u,d+1,dd+c);
	}
}
int Jump(int u,int d)
{
	for(int j=19;j>=0;j--)
	{
		if((1<<j)&d)
		{
			u=pa[u][j];
		}
	}
	return u;
}
int lca(int u,int v)
{
	if(dep[u]<dep[v]) swap(u,v);
	u=Jump(u,dep[u]-dep[v]);
	if(u==v) return u;
	for(int i=19;i>=0;i--)
	{
		if(pa[u][i]!=pa[v][i])
		{
			u=pa[u][i];
			v=pa[v][i];
		}
	}
	return pa[u][0];
}
int main()
{
	int n,m;
	int t;
	cin>>t;
	while(t--)
	{
		scanf("%d%d",&n,&m);
		for(int i=1;i<=n;i++) G[i].clear();
		for(int i=1;i<n;i++)
		{
			int u,v,w;
			scanf("%d%d%d",&u,&v,&w);
			G[u].push_back(node(u,v,w));
			G[v].push_back(node(v,u,w));
		} 
		dfs(1,1,0,0);
		while(m--)
		{
			int u,v;
			scanf("%d%d",&u,&v);
			printf("%d\n",dis[u]+dis[v]-2*dis[lca(u,v)]);
		}
	}
}

Balanced Lineup

 POJ - 3264 

For the daily milking, Farmer John's N cows (1 ≤ N ≤ 50,000) always line up in the same order. One day Farmer John decides to organize a game of Ultimate Frisbee with some of the cows. To keep things simple, he will take a contiguous range of cows from the milking lineup to play the game. However, for all the cows to have fun they should not differ too much in height.

Farmer John has made a list of Q (1 ≤ Q ≤ 200,000) potential groups of cows and their heights (1 ≤ height ≤ 1,000,000). For each group, he wants your help to determine the difference in height between the shortest and the tallest cow in the group.

Input

Line 1: Two space-separated integers, N and Q
Lines 2.. N+1: Line i+1 contains a single integer that is the height of cow i 
Lines N+2.. NQ+1: Two integers A and B (1 ≤ A ≤ B ≤ N), representing the range of cows from A to B inclusive.

Output

Lines 1.. Q: Each line contains a single integer that is a response to a reply and indicates the difference in height between the tallest and shortest cow in the range.

Sample Input

6 3
1
7
3
4
2
5
1 5
4 6
2 2

Sample Output

6
3
0

RMQ裸题,可以线段树处理,也可以直接ST处理,这里使用了刘汝佳的白书上的模板。

#include<cstdio>
#include<iostream>
#include<vector>
using namespace std;
const int size=5e4+5;
int dmax[size][20],dmin[size][20];
void RMQ_init(const vector<int> &A)
{
	int n=A.size();
	for(int i=0;i<n;i++) dmax[i][0]=A[i],dmin[i][0]=A[i];
	for(int j=1;(1<<j)<=n;j++)
	{
		for(int i=0;i+(1<<j)-1<n;i++)
		{
			dmax[i][j]=max(dmax[i][j-1],dmax[i+(1<<(j-1))][j-1]);
			dmin[i][j]=min(dmin[i][j-1],dmin[i+(1<<(j-1))][j-1]);
		}
	}
}
int RMQ(int L,int R)
{
	int k=0;
	while((1<<(k+1))<=R-L+1) k++;
	return max(dmax[L][k],dmax[R-(1<<k)+1][k])-min(dmin[L][k],dmin[R-(1<<k)+1][k]);
}
int main()
{
	int n,m;
	while(~scanf("%d%d",&n,&m))
	{
		int num;
		vector<int> V;
		while(n--) scanf("%d",&num),V.push_back(num);
		RMQ_init(V);
		while(m--)
		{
			int l,r;
			scanf("%d%d",&l,&r);
			printf("%d\n",RMQ(l-1,r-1));
		}
	}
}

Frequent values

 UVA - 11235 

白书上的原题,这里招搬白书上的讲解:

“应注意到整个数组是非降序的,所有相等的元素都会聚到一起。这样就可以把整个数组进行游程编码(Run Length Encoding,RLE)。比如-1,1,1,2,2,2,4”就可以编码成(-1,1)(1,2)(2,3)(4,1),其中(a,b)代表有b个连续的a。用value[i]和count[i]分别表示第i段的数值和出现次数,num[p],left[p],right[p]分别表示位置p所在段的编号与左右端点的位置,则下图情况:

每次查询(L,R)的结果为以上三部分的最大值:从L到L所在段的结束处的元素个数(即right[L]-L+1)、从R所在段的开始处到R处的元素个数(R-left[R]+1)、中间第num[L]+1段到num[R]-1段的count的最大值。

特殊情况:如果L,R在同一段中,则答案是R-L+1

AC代码:

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
const int size=1e5+5;
const int inf=0x3f3f3f3f;
int value[size],count[size];
int eft[size],ight[size];
int d[size][20];
int RMQ(int L,int R)
{
	int k=0;
	while((1<<(k+1))<=R-L+1) k++;
	return max(d[L][k],d[R-(1<<k)+1][k]);
}
int main()
{
	int n,q;
	int num[size],left[size],right[size];
	while(~scanf("%d",&n)&&n)
	{
		memset(count,0,sizeof(count));
		scanf("%d",&q);
		int tot=0;
		int Now,Last=-inf;
		for(int i=1;i<=n;i++)
		{
			scanf("%d",&Now);
			if(Now!=Last)
			{
				tot++;
				value[tot]=Now;
				left[tot]=i;
				right[tot-1]=i-1;
				Last=Now;
			}
			num[i]=tot;
			count[tot]++;
		} 
		right[tot]=n;		
		for(int i=1;i<=tot;i++) d[i][0]=count[i];
		for(int j=1;(1<<j)<=n;j++)
		{
			for(int i=0;i+(1<<j)-1<n;i++)
			{
				d[i][j]=max(d[i][j-1],d[i+(1<<(j-1))][j-1]);
			}
		}
		for(int i=1;i<=n;i++)
		{
			eft[i]=left[num[i]];
			ight[i]=right[num[i]];
			//cout<<eft[i]<<' '<<ight[i]<<endl;
		}
		while(q--)
		{
			int l,r;
			scanf("%d%d",&l,&r);
			if(num[l]==num[r]) printf("%d\n",r-l+1);
			else
			{
				int Fir=ight[l]-l+1,Sec=r-eft[r]+1;
//				cout<<Fir<<' '<<Sec<<endl;
				int ans=max(Fir,Sec);
				int L=num[l]+1,R=num[r]-1;
				if(L<=R) ans= max(RMQ(L,R),ans);
//				cout<<L<<' '<<R<<endl;
				printf("%d\n",ans);
			}
		}
		
	}
}

Counting Offspring

 HDU - 3887 

You are given a tree, it’s root is p, and the node is numbered from 1 to n. Now define f(i) as the number of nodes whose number is less than i in all the succeeding nodes of node i. Now we need to calculate f(i) for any possible i.

Input

Multiple cases (no more than 10), for each case: 
The first line contains two integers n (0<n<=10^5) and p, representing this tree has n nodes, its root is p. 
Following n-1 lines, each line has two integers, representing an edge in this tree. 
The input terminates with two zeros.

Output

For each test case, output n integer in one line representing f(1), f(2) … f(n), separated by a space.

Sample Input

15 7
7 10
7 1
7 9
7 3
7 4
10 14
14 2
14 13
9 11
9 6
6 5
6 8
3 15
3 12
0 0

Sample Output

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

dfs序裸题加上树状数组维护,不解释。

AC代码:

#include<iostream>
#include<vector>
#include<cstdio>
#include<cstring>
#define lowbit(x) (x&(-x))
using namespace std;
const int size=1e5+5;
vector<int > G[size];
int id[size];
int out[size];
int cnt[2*size];
int ans[size];
int tot;
void add(int x,int num)
{
	while(x<=tot)
	{
		cnt[x]+=num;
		x+=lowbit(x);
	}
}
int query(int x)
{
	int ans=0;
	while(x>0)
	{
		ans+=cnt[x];
		x-=lowbit(x);
	} 
	return ans;
}
void dfs(int p,int fa)
{
//	pi[p]=tot;
	id[p]=++tot; 
	for(int i=0;i<G[p].size();i++)
	{
		int nxt=G[p][i];
		if(nxt==fa) continue;
		dfs(nxt,p);
	}
	out[p]=++tot;
}

void init(int n)
{
	for(int i=1;i<=n;i++) G[i].clear();
	memset(cnt,0,sizeof(cnt));
	tot=0;
}
int main()
{
	int n,m;
	while(~scanf("%d%d",&n,&m)&&n)
	{
		init(n);
		for(int i=0;i<n-1;i++)
		{
			int u,v;
			scanf("%d%d",&u,&v);
			G[u].push_back(v);
			G[v].push_back(u);
		} 
		dfs(m,0);
		for(int i=1;i<tot;i++) add(i,1);
		for(int i=n;i>0;i--)
		{
			ans[i]=(query(out[i]-1)-query(id[i]))/2;
			add(out[i],-1);
			add(id[i],-1);
		}
		for(int i=1;i<=n;i++)
		{
			printf("%d",ans[i]);
			if(i==n) printf("\n");
			else printf(" ");
		}
	}
	return 0;
}

树的统计Count

 HYSBZ - 1036 

 一棵树上有n个节点,编号分别为1到n,每个节点都有一个权值w。我们将以下面的形式来要求你对这棵树完成
一些操作: I. CHANGE u t : 把结点u的权值改为t II. QMAX u v: 询问从点u到点v的路径上的节点的最大权值 I
II. QSUM u v: 询问从点u到点v的路径上的节点的权值和 注意:从点u到点v的路径上的节点包括u和v本身

Input

  输入的第一行为一个整数n,表示节点的个数。接下来n – 1行,每行2个整数a和b,表示节点a和节点b之间有
一条边相连。接下来n行,每行一个整数,第i行的整数wi表示节点i的权值。接下来1行,为一个整数q,表示操作
的总数。接下来q行,每行一个操作,以“CHANGE u t”或者“QMAX u v”或者“QSUM u v”的形式给出。 
对于100%的数据,保证1<=n<=30000,0<=q<=200000;中途操作中保证每个节点的权值w在-30000到30000之间。

Output

  对于每个“QMAX”或者“QSUM”的操作,每行输出一个整数表示要求输出的结果。

Sample Input

4 1 2 2 3 4 1 4 2 1 3 12 QMAX 3 4 QMAX 3 3 QMAX 3 2 QMAX 2 3 QSUM 3 4 QSUM 2 1 CHANGE 1 5 QMAX 3 4 CHANGE 3 6 QMAX 3 4 QMAX 2 4 QSUM 3 4

Sample Output

4 1 2 2 10 6 5 6 5 16

树链剖分的裸题,剖分完了之后再用线段树维护即可。不得不吐槽一下,树链剖分实在是码量惊人。

#include<iostream>
#include<cstdio>
#include<vector>
#include<cstring>
#define md(l,r) (l+r)>>1
#define lson(x) x<<1
#define rson(x) x<<1|1
using namespace std;
const int size=60005;
const int inf=0x3f3f3f3f;
struct node{
	int sum;
	int maxn;
	int l,r;
}tree[size<<2];
int date[size];
int fa[size],sz[size],dep[size];
int id[size];
int son[size];
int Rank[size];
vector<int> G[size];
int top[size];
int tot=0;
void build(int k,int l,int r)
{
	tree[k].l=l,tree[k].r=r;
	if(l==r)
	{
		tree[k].maxn=date[Rank[l]];
		tree[k].sum=date[Rank[l]];
		return ;
	}
	int mid=md(l,r);
	build(lson(k),l,mid);
	build(rson(k),mid+1,r);
	tree[k].sum=tree[lson(k)].sum+tree[rson(k)].sum;
	tree[k].maxn=max(tree[lson(k)].maxn,tree[rson(k)].maxn);
}
int  query_max(int k,int l,int r)
{
	int ans=0;
//	cout<<l<<' '<<r<<' '<<tree[k].l<<' '<<tree[k].r<<endl;
	if(l==tree[k].l&&r==tree[k].r)
	{
		return tree[k].maxn;
	}
	int mid=(tree[k].l+tree[k].r)>>1;
	if(l>=mid+1)
	{
		ans=query_max(rson(k),l,r);
	}
	else if(r<=mid)
	{
		ans=query_max(lson(k),l,r);
	}
	else
	{
		ans=max(query_max(lson(k),l,mid),query_max(rson(k),mid+1,r));
	}
	return ans;
}
int query_sum(int k,int l,int r)
{
	int ans=0;
	if(l==tree[k].l&&r==tree[k].r)
	{
		return tree[k].sum;
	}
	int mid=md(tree[k].l,tree[k].r);
	if(l>=mid+1)
	{
		ans=query_sum(rson(k),l,r);
	}
	else if(r<=mid)
	{
		ans=query_sum(lson(k),l,r);
	} 
	else
	{
		ans=query_sum(lson(k),l,mid)+query_sum(rson(k),mid+1,r);
	}
	return ans;
}
void change(int k,int d,int x)
{
	if(tree[k].l==d&&tree[k].r==d)
	{
		tree[k].maxn=x;
		tree[k].sum=x;
		return ;
	}
	int mid=(tree[k].l+tree[k].r)>>1;
	if(d>=tree[k].l&&d<=mid) change(lson(k),d,x);
	else change(rson(k),d,x);
	tree[k].maxn=max(tree[lson(k)].maxn,tree[rson(k)].maxn);
	tree[k].sum=tree[lson(k)].sum+tree[rson(k)].sum;
}
void dfs1(int u,int F,int d)
{
	fa[u]=F;
	dep[u]=d;
	sz[u]=1;
	for(int i=0;i<G[u].size();i++)
	{
		int e=G[u][i];
		if(e==F) continue;
		dfs1(e,u,d+1);
		sz[u]+=sz[e];
		if(son[u]==-1||sz[son[u]]<sz[e]) son[u]=e;
	}
}
void dfs2(int u,int tp)
{
	top[u]=tp;
	id[u]=++tot;
	//cout<<u<<' '<<tot<<endl;
	Rank[tot]=u;
	if(son[u]==-1) return ;
	dfs2(son[u],tp);
	for(int i=0;i<G[u].size();i++)
	{
		int e=G[u][i];
		if(e==fa[u]||e==son[u]) continue;
		dfs2(e,e);
	}
}
int query_sum_line(int u,int v)
{
	int fu=top[u],fv=top[v],ans=0;
	while(fu!=fv)
	{
		if(dep[fu]<dep[fv]){
			swap(fu,fv);
			swap(u,v);
		}
		ans+=query_sum(1,id[fu],id[u]);
		u=fa[fu];
		fu=top[u];
	}
	if(dep[u]>dep[v]) swap(u,v);
	ans+=query_sum(1,id[u],id[v]);
	return ans;
}
int query_max_line(int u,int v)
{
	int fu=top[u],fv=top[v],ans=-inf;
	while(fu!=fv)
	{
//		cout<<u<<endl;
		if(dep[fu]<dep[fv]){
			swap(fu,fv);
			swap(u,v);
		}
		ans=max(ans,query_max(1,id[fu],id[u]));
		u=fa[fu];
		fu=top[u];
	}
	if(dep[u]>dep[v]) swap(u,v);
	ans=max(ans,query_max(1,id[u],id[v]));
	return ans;
}
void init()
{
	int n;
	scanf("%d",&n);
	memset(son,-1,sizeof(son));
	for(int i=1;i<=n;i++) G[i].clear();
	for(int i=1;i<n;i++)
	{
		int u,v;
		scanf("%d%d",&u,&v);
		G[u].push_back(v);
		G[v].push_back(u); 
	} 
	for(int i=1;i<=n;i++) scanf("%d",&date[i]);
	tot=0;
	dfs1(1,-1,0);
	dfs2(1,1);
	build(1,1,tot);
}
int main()
{
	init();
	int q;
	scanf("%d",&q);
	//cout<<'x'<<endl;
	while(q--)
	{
		char op[10];
		scanf("%s",op);
		if(op[1]=='M')
		{
			int l,r;
			scanf("%d%d",&l,&r);
			printf("%d\n",query_max_line(l,r));
		} 
		else if(op[1]=='H')
		{
			int p,val;
			scanf("%d%d",&p,&val);
			change(1,id[p],val);
		} 
		else
		{
			int l,r;
			scanf("%d%d",&l,&r);
			printf("%d\n",query_sum_line(l,r));
		}
	} 
} 

猜你喜欢

转载自blog.csdn.net/baiyifeifei/article/details/81876802