CF #294

这次比赛的题目总体来讲比较水 因为我都做出了4个题 = =。

A B C题

这三个题没什么可说的太简单了= =。

D题

利用前缀和的思想,先预处理所有的前缀和,然后看开头字母相同的有没有前缀和相同的 有就是一个答案。需要注意 题目中会出现long long作下标的情况,因此要使用map

E题

这是一个LCA的应用,由于所求涉及到割级祖先,使用在线倍增算法求解。

#pragma comment (linker, "/STACK:1024000000, 1024000000")
#include <vector>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>

using namespace std;

const int MAX = 100010;
int n,m;
int tot, head[MAX];
int deep[MAX],father[MAX],size[MAX],p[MAX][20];

struct edge{
	int v,next;
}node[MAX << 1];

void init(){
	tot = 0;
	memset(head, -1, sizeof(head));
}

void addedge(int u, int v){
	node[tot].v = v;
	node[tot].next = head[u];
	head[u] = tot++;
}

void dfs(int u, int pre, int t){
	size[u] = 1;//记录以u为根的结点有多少个
	deep[u] = t;//记录深度
	father[u] = pre;//记录父亲
	for(int i=head[u]; i!=-1; i=node[i].next){
		int v = node[i].v;
		if(v != pre){
			dfs(v, u, t+1);
			size[u] += size[v];
		}
	}
}

void make_parent(){//预处理各级祖先 也是倍增的体现
	for(int i=1; i<=n; i++) p[i][0] = father[i];
	for(int j=1; (1<<j)<=n; j++)
		for(int i=1; i<=n; i++)
			p[i][j] = p[p[i][j-1]][j-1];
}

int make_ancestor(int u, int dif){//求解跳到哪个祖先处
	int ret = u;
	for(int i=0; i<20; i++)
		if(dif & (1 << i)) ret = p[ret][i];
	return ret;
}

int lca(int a, int b){//求解LCA过程
	if(deep[a] < deep[b]) swap(a, b);
	for(int j=19; j>=0; j--)  
        if(deep[a] - (1 << j) >= deep[b])  
            a = p[a][j];
    if(a == b) return a;
	for(int j=19; j>=0; j--){
		if(p[a][j] != p[b][j]){
			a = p[a][j];
			b = p[b][j];
		}
	}
	return p[a][0];
}

int query(int a, int b){
	if(a == b) return n;//注意如果是同一个点,那么所有点都满足要求,直接返回
	if(deep[a] < deep[b]) swap(a, b);
	int c = lca(a, b);
	int da = deep[a] - deep[c];
	int db = deep[b] - deep[c];
	int sum = da + db;
	if(sum & 1) return 0;//因为从a到b一定会经过它们的lca,因此如果路程是奇数,一定不存在满足条件的点
	if(deep[a] == deep[b]){//如果所求两个点深度相同,它们到lca的距离必然相同
		int fa = make_ancestor(a, da - 1);//a跳到lca的最近的子节点
		int fb = make_ancestor(b, db - 1);//b也跳到lca的最近的字节点
		return n - size[fa] - size[fb];//那么答案就是所有结点中除掉以fa,fb为根节点的结点的数量
	}
	else{//如果深度不同的话
		sum >>= 1;//找到a到b路径上的中点,它是一个满足条件的点,同时它不在路径上的其他子结点以及子树上的点都是符合条件的
		int dx = make_ancestor(a, sum);
		int dy = make_ancestor(a, sum - 1);
		return size[dx] - size[dy];
	}
}

int main(){
	scanf("%d",&n);
	init();
	for(int i=0; i<n-1; i++){
		int u, v;
		scanf("%d%d",&u,&v);
		addedge(u, v);
		addedge(v, u);
	}
	scanf("%d",&m);
	dfs(1, 0, 0);
	make_parent();
	for(int i=0; i<m; i++){
		int u, v;
		scanf("%d%d",&u,&v);
		printf("%d\n",query(u, v));
	}
	return 0;
}



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章