Learning notes - point divide and conquer

Posted by rekha on Thu, 20 Jan 2022 15:18:45 +0100

preface

It's another algorithm that everyone knows. I can't just climb to learn~

realization

Point divide and conquer is usually used to solve the problem of path statistics on the tree.

In fact, the idea of point divide and conquer is very simple: we consider a tree with roots. Obviously, we can divide its paths into two categories: one is through the root and the other is not through the root. According to this, we can get a powerful idea: for the current root node, directly calculate its information to each point in the subtree and make statistics, and then merge the information of each subtree. Finally, each subtree is directly recursive to solve the sub problem, so as to reduce some unnecessary operations.

But the idea soon became false. The key point is how to determine the node as the root. We relate various properties of the tree and find that the center of gravity is optimal.

So our approach is to find the center of gravity, deal with the path through the center of gravity, divide and conquer the sub trees of the center of gravity and find the answer.

Find the center of gravity

int rt,S,rtmx; bool used[MAXN];
int find(int x,int fa){
	int mx=0,siz=1,son;
	for(Edge s:e[x]){
		if(s.to==fa||used[s.to]) continue;
		siz+=(son=find(s.to,x));
		mx=max(mx,son);
	}mx=max(mx,S-siz);
	if(rtmx>mx) rt=x,rtmx=mx;
	return siz;
}

Explain that rt stores the barycenter, and S stores the size of the tree currently looking for the barycenter (because you need to process the barycenter of the subtree to continue the divide and conquer every time you recurse to the subtree processing). used [] indicates whether this point has been deleted as the barycenter (obviously, you can't count the points in other subtrees after the barycenter divide and conquer).

Everything else is very well understood. Complexity \ (O(S) \).

Point divide and conquer

void Divid(int x){
	used[x]=1;
	for(auto s:e[x]){
		if(used[s.to]) continue;
		//Dealing with current issues
	}init();
	for(auto s:e[x]){
		if(used[s.to]) continue;
		S=Siz(s.to,s.to);rtmx=inf;find(s.to,s.to);
		Divid(rt)//Recursive processing subtree
	}
}

Examples

The core of feeling is thought, just like dsu on tree, so I still need to write more questions.

P3806 [template] point divide and conquer 1
The template is disgusting ~ consider using the bucket to count whether the node with the current root distance of \ (i \) appears.

$\texttt{Code}$
#include<bits/stdc++.h>
#define ll long long
#define inf (1<<30)
#define INF (1ll<<60)
#define pb push_back
using namespace std;
const int MAXN=1e5+10;
struct Edge{int to,w;};
vector<Edge> e[MAXN];
int rt,S,rtmx; bool used[MAXN];
int find(int x,int fa){
	int mx=0,siz=1,son;
	for(Edge s:e[x]){
		if(s.to==fa||used[s.to]) continue;
		siz+=(son=find(s.to,x));
		mx=max(mx,son);
	}mx=max(mx,S-siz);
	if(rtmx>mx) rt=x,rtmx=mx;
	return siz;
}
int Siz(int x,int fa){
	int ret=1;
	for(Edge s:e[x]){
		if(s.to==fa||used[s.to]) continue;
		ret+=Siz(s.to,x);
	}return ret;
}
const int MAXM=1e7+10;
bool ans[MAXM];
int K;
vector<int> val,Ap;
void getdis(int x,int fa,int len){
	if(len>K) return;
	val.pb(len);
	for(auto s:e[x]){
		if(s.to==fa||used[s.to]) continue;
		getdis(s.to,x,len+s.w);
	}
}
void init(){while(!Ap.empty()) ans[Ap.back()]=0,Ap.pop_back();}
bool Divid(int x){
	used[x]=1;
	for(auto s:e[x]){
		if(used[s.to]) continue;
		getdis(s.to,x,s.w);
		for(auto W:val) if(ans[K-W]){init();val.clear();used[x]=0;return 1;}
		while(!val.empty()){
			if(!ans[val.back()]) ans[val.back()]=1,Ap.pb(val.back());
			val.pop_back();
		}
	}init();
	for(auto s:e[x]){
		if(used[s.to]) continue;
		S=Siz(s.to,s.to);rtmx=inf;find(s.to,s.to);
		if(Divid(rt)){used[x]=0;return 1;}
	}used[x]=0;return 0;
}
int main()
{
//	freopen("P3806_1.in","r",stdin);
//	freopen("my.out","w",stdout);
	int n,m;
	scanf("%d%d",&n,&m);
	for(int i=2,u,v,w;i<=n;i++){
		scanf("%d%d%d",&u,&v,&w); 
		e[u].pb((Edge){v,w});
		e[v].pb((Edge){u,w});
	}ans[0]=1;
	S=n;rtmx=inf;find(1,0);
	int Rt=rt;
	while(m--){
		scanf("%d",&K);
		if(Divid(Rt)) puts("AYE");
		else puts("NAY");
	}
}

Topics: Algorithm