Divide and conquer learning and examples

Divide and conquer is a class of algorithms used to process paths in trees.
Point divide and conquer, that is, divide and conquer the points on the tree. The essence of point divide and conquer is the process of splitting a tree into multiple subtrees, and then continuously splitting it down. Before dividing and conquering by point, we must first find a point, and it will be better for us to divide and conquer from this point. So how to get this point? It must be a relatively balanced point. Balance means that the difference between the sizes of the subtrees at this point is as small as possible. This equilibrium point is called the center of gravity of the tree.
Formal definition of the center of gravity of a tree:
If there is a point whose largest subtree has the least number of nodes among all its subtrees, then this point is the center of gravity of the tree. (It is very obvious that if we divide and conquer from this point down, the depth of the whole tree will be shallower, so the time efficiency is higher)
The method of finding the center of gravity is very simple, which is a dfs.
void getroot(int x,int fa){
    f[x]=0;siz[x]=1;
    for(int i=head[x];i!=-1;i=nxt[i]){
        int to=L[i].to;
        if(to==fa||vis[to]) continue;
        getroot(to,x);f[x]=max(f[x],siz[to]);
        siz[x]+=siz[to];
    }
    f[x]=max(f[x],sum-siz[x]);
    //关注一下这一句,因为我们若以点x为根,则还有一棵子树,就是x和上面(深度小于它)的所有点
    if(f[x]<f[root]) root=x;
}
Implementation of divide and conquer:
To achieve divide and conquer, we must first perform depth preprocessing, which is the following getdep function. Getdep is to find the distance from a point below it to the root with x as the root.
void getdep(int x,int fa){
    dep[++dep[0]]=d[x];
    for(int i=head[x];i!=-1;i=nxt[i]){
        int to=L[i].to;
        if(vis[to]||to==fa) continue;
        d[to]=d[x]+L[i].val;getdep(to,x);
    }
}
Then there is our solve function, which is also a step-by-step divide and conquer.
The following is the general formula of solve, where cal is the function of the calculated value, and the second parameter of cal represents the distance from the point to the current center of gravity. We just added cal(x,0) at the beginning. That is, add up all the conditions below. But there are some situations that are illegal, so if I want to subtract it, I also subtract the situation of cal(son[j],w[j]). Because we want to deal with the case of passing through x, it is possible to satisfy the situation at two points in the subtree of x, but their LCA is not x, that is, they do not pass through x. Then this situation is illegal at x, but we added it, so we can subtract cal(son[j], w[j]).
void solve(int x){
    ans+=cal(x,0);vis[x]=1;
    for (int j=lnk[x];j;j=net[j])
    if (!vis[son[j]]){
        ans-=cal(son[j],w[j]);
        sum=siz[son[j]];
        root=0;
        getroot(son[j],0);
        solve(root);
    }
}
When there is enough time, we can open a queue, and then pair the points in different subtrees of x (this avoids illegal situations).

Example: BZOJ2152

Description

Cong Cong and Coco are brothers, they often fight over trivial matters, such as only the last ice lolly left at home and both want to eat it, and both want to play with the computer (but they only have one computer at home)… …In this kind of problem, rock-paper-scissors is usually fine, but they are tired of playing this kind of low-IQ game. Their dad was getting bored by their quarrels, so he invented a new game: Dad draws n "dots" on the paper, and uses n-1 "edges" to connect these n "dots" exactly ( In fact, this is a tree). And each "edge" has a number on it. Next, Cong Cong and Coco select a point at random (of course, they can't see the tree when they select the point). If the sum of all the edges between the two points is exactly a multiple of 3, then the decision is made. Cong Cong wins, otherwise Coco wins. Cong Cong likes to think about problems very much. After each game, he will study the tree carefully, hoping to know what his probability of winning is for this picture. Now please help to find this value to verify whether Cong Cong's answer is correct.

Input

Line 1 of the input contains 1 positive integer n. Next n-1 lines, each line contains 3 integers x, y, w, indicating that there is an edge between point x and point y, and the number above is w.

Output

Output this probability as a fractional reduction (i.e. in the form "a/b", where a and b must be relatively prime. If the probability is 1, output "1/1").

Sample Input

5

1 2 1

1 3 2

1 4 1

2 5 3

Sample Output

13/25

【Example description】

The 13 sets of point pairs are (1,1) (2,2) (2,3) (2,5) (3,2) (3,3) (3,4) (3,5) (4,3 ) (4,4) (5,2) (5,3) (5,5).

【Data scale】

For 100% of the data, n<=20000.


This problem is very obvious that it can be divided and conquered by points. What we require is the number of paths on all trees that are multiples of 3. First, each point becomes a path alone, and the length is 0, which must satisfy the condition, so ans=n. Then we will divide and conquer by point. When dividing and conquering by point, we can not use the above solve, because there are only three cases, x≡0(mod 3), x≡1(mod 3), x≡2( mod 3), so we directly open an array of size 3, classify the distance from the path to x by 3 modulo, and finally accumulate it.

Code:

#include<bits/stdc++.h>
#define MAXN 20005
using namespace std;
int read(){
    char c;int x;while(c=getchar(),c<'0'||c>'9');x=c-'0';
    while(c=getchar(),c>='0'&&c<='9') x=x*10+c-'0';return x;
}
int n,cnt,sum,root,base,ans,top,gcdnum;
int head[MAXN<<1],nxt[MAXN<<1],f[MAXN],siz[MAXN],vis[MAXN],dep[MAXN],d[MAXN],hsh[3],que[MAXN];
struct node{
    int to,val;
}L[MAXN<<1];
int gcd(int a,int b){return b?gcd(b,a%b):a;}
void add(int x,int y,int c){
    L[cnt]=(node){y,c};
    nxt[cnt]=head[x];head[x]=cnt;cnt++;
    L[cnt]=(node){x,c};
    nxt[cnt]=head[y];head[y]=cnt;cnt++;
}
void getroot(int x,int fa){
    f[x]=0;siz[x]=1;
    for(int i=head[x];i!=-1;i=nxt[i]){
        int to=L[i].to;
        if(to==fa||vis[to]) continue;
        getroot(to,x);f[x]=max(f[x],siz[to]);
        siz[x]+=siz[to];
    }
    f[x]=max(f[x],sum-siz[x]);
    if(f[x]<f[root]) root=x;
}
void getdep(int x,int fa){
    dep[++dep[0]]=d[x];
    for(int i=head[x];i!=-1;i=nxt[i]){
        int to=L[i].to;
        if(vis[to]||to==fa) continue;
        d[to]=d[x]+L[i].val;getdep(to,x);
    }
}
void solve(int x){
    vis[x]=1;top=0;hsh[0]=1;
    for(int i=head[x];i!=-1;i=nxt[i]){
        int to=L[i].to;
        if(vis[to]) continue;
        dep[0]=0;d[to]=L[i].val;getdep(to,x);
        for(int j=1;j<=dep[0];j++)
          ans+=hsh[(3-(dep[j]%3))%3]*2;   //ans加上一个被3模与它相反的数
        for(int j=1;j<=dep[0];j++) hsh[que[++top]=dep[j]%3]++;
    }
    for(int i=1;i<=top;i++) hsh[que[i]]--;
    for(int i=head[x];i!=-1;i=nxt[i]){
        int to=L[i].to;
        if(vis[to]) continue;
        root=0;sum=siz[to];
        getroot(to,x);
        solve(root);
    }
}
int main()
{
    memset(head,-1,sizeof(head));
    n=read();ans=n;
    for(int i=1;i<n;i++){
        int x=read(),y=read(),z=read();
        add(x,y,z);
    }
    f[0]=2e9;sum=n;root=0;
    getroot(1,0);
    solve(root);
    base=n*n;gcdnum=gcd(ans,base);
    printf("%d/%d",ans/gcdnum,base/gcdnum);
    return 0;
}

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=325614380&siteId=291194637