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;
}



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