Understand but not fully understand long chain subdivision

Posted by weemee500 on Fri, 28 Jan 2022 11:34:17 +0100

Long chain subdivision

On the whole, it is very similar to heavy chain dissection

First, define the heavy son as: the son with the deepest subtree depth

Then the rest is the light son

The heavy side is connected to the heavy son, and the heavy side is connected into a heavy chain

Each point is in a unique long chain, and the long chain must not intersect

The realization is similar to heavy chain subdivision

len records the deepest depth that can be reached

void dfs(int u,int _f){
	fe(i,u) {
	    int v = e[i].to;
		if (v != _f){
			dfs(v,u);
			if(len[v] > len[son[u]]) 
		        son[u] = v;
		}
	}
	len[u] = len[son[u]] + 1;
}

Optimize DP

When maintaining depth related information, we should quickly inherit the information from the son, and then count the information from the son with violence, which is very similar to dsu on tree

This is not particularly clear about depth. In fact, it is the feeling that subscript is related to depth, not anything else

Because each point belongs to only one long chain and all the light sons are the top of the long chain, the time complexity is linear

CF161D Distance in Tree

\(\texttt{Link.}\)

Find the number of paths with exactly \ (k \) length in a rootless tree

Dot divide and conquer is \ (\ mathrm{O}(n\log n) \), can it be better?

Consider the long chain subdivision to optimize the simple tree DP

Let \ (f_{i,j} \) represent the number of nodes whose distance from \ (I \) to \ (I \) is \ (j \)

Because \ (k \le 500 \), the principle of violent transfer and then multiplication can pass this problem

Complexity \ (\ mathrm{O} (nk) \)

The optimized complexity is \ (\ mathrm{O} (n) \)

For the convenience of inheriting information, use the pointer ptr[i] to point to the information inherited by the point \ (I \)

This also makes the statistical information only use an array, and the pointer only represents the first position of a node information

Then this thing is as fast and outrageous as my tree array + divide and conquer because of violence

The optimal solution of this problem is dsu on tree. Do you do whatever you want when the constant is small

Code :

int head[N],ecnt = -1;
struct Edge {
    int nxt,to;
}e[N << 1];
inline void AddEdge(int st,int ed) {
    e[++ecnt] = (Edge) {head[st],ed},head[st] = ecnt;
    e[++ecnt] = (Edge) {head[ed],st},head[ed] = ecnt;
}

int k;

int ans;
int fa[N];
int len[N],hson[N];
int *ptr[N],buc[N];
int cnt;

void dfs1(int u,int _f) {
    fa[u] = _f;
    fe(i,u) {
        int v = e[i].to;
        if(v == _f) continue;
        dfs1(v,u);
        if(len[v] > len[hson[u]])
            hson[u] = v;
    }
    len[u] = len[hson[u]] + 1;
}

inline void Add(int u,int v) {
    ff(i,std::max(0,k - len[u]),std::min(len[v],k))
        ans += ptr[v][i] * ptr[u][k - i - 1];
    ff(i,0,len[v])
        ptr[u][i + 1] += ptr[v][i];
}

void dfs2(int u,int top) {
    if(u == top) {
        ptr[u] = buc + cnt;
        cnt += len[u];
    }
    else ptr[u] = ptr[fa[u]] + 1;
    fe(i,u) {
        int v = e[i].to;
        if(v == fa[u] || v == hson[u])
            continue;
        dfs2(v,v);
    }
    if(hson[u]) dfs2(hson[u],top);
    if(k < len[u]) ans += ptr[u][k];
    ++ptr[u][0];
    fe(i,u) {
        int v = e[i].to;
        if(v == fa[u] || v == hson[u])
            continue;
        Add(u,v);
    }
}

CF1009F Dominant Indices

It seems that this question is more convenient than dsu on tree

[POI2014] HOT-Hotels

This question is different from the above two questions and cannot be combined with tree heuristic

\(\texttt{Link1.}\)

\(\texttt{Link2.}\)

For those not strengthened, violence \ (\ mathrm{O} (n^2)\) DP is enough

But the enhanced version won't work

Then design the status:

\(f_{i, j} \): the number of points within the \ (I \) subtree whose distance to \ (I \) is \ (j \)

\(g_{i, j} \): the logarithm of points within the \ (I \) subtree whose distance to \ (I \) is \ (j \)

Then the principle of multiplication

It seems to be too hasty

Look at the code

Did I get wind_ Whosper syndrome

Code :

int head[N],ecnt = -1;
struct Edge {
    int nxt,to;
}e[N << 1];
inline void AddEdge(int st,int ed) {
    e[++ecnt] = (Edge) {head[st],ed},head[st] = ecnt;
    e[++ecnt] = (Edge) {head[ed],st},head[ed] = ecnt;
}

int n;
int len[N],son[N];
ll tmp[N << 2];
ll *f[N],*g[N],*id = tmp;
ll ans;

void dfs(int u,int _f){
	fe(i,u) {
	    int v = e[i].to;
		if (v != _f){
			dfs(v,u);
			if(len[v] > len[son[u]]) 
		        son[u] = v;
		}
	}
	len[u] = len[son[u]] + 1;
}

void dp(int u,int _f){
	if(son[u]) {
	    f[son[u]] = f[u] + 1;
	    g[son[u]] = g[u] - 1;
	    dp(son[u],u);
	}
	f[u][0] = 1;
	ans += g[u][0];
	fe(i,u) {
		int v = e[i].to;
		if(v == _f || v == son[u]) continue;
		f[v] = id;
		id += len[v] << 1;
		g[v] = id;
		id += len[v] << 1;
		dp(v,u);
		ff(j,0,len[v] - 1){
			if(j) ans += f[u][j - 1] * g[v][j];
			ans += g[u][j + 1] * f[v][j];
		}
		ff(j,0,len[v] - 1){
			g[u][j + 1] += f[u][j + 1] * f[v][j];
			if(j) g[u][j - 1] += g[v][j];
			f[u][j + 1] += f[v][j];
		}
	}
}

int mian() {
    init_IO();
    mems(head,-1);
	n = read();
	ff(i,1,n - 1) AddEdge(read(),read());
	dfs(1,1);
	f[1] = id;
	id += len[1] << 1;
	g[1] = id;
	id += len[1] << 1;
	dp(1,0);
	write(ans);
	end_IO();
	return 0;
}

Optimization greed

BZOJ 3252 introduction

\(\texttt{Link.}\)

Summary of topic meaning:

A tree with \ (n \) points has roots and points have point weights. Select \ (k \) simple paths from the root node to the leaf node to maximize the sum of point weights of the coalescence of these paths

Find the weight sum of this point

Then you can find that this structure is very similar to the structure of the long chain. The long chain also does not intersect, so the contribution will not be counted many times, and the long chain also reaches the leaf node

So take the one with large weight, find the first \ (k \) and open a heap

Find k-level ancestors

\(\texttt{Link.}\)

Given a rooted tree, multiple queries are given. The form \ (u,k \) represents the ancestor of jumping up \ (k \) nodes from the seeking point \ (u \)

First of all, the direct tree multiplication is \ (< \ mathrm {o} (n \ log n), \ mathrm {o} (\ log n) > \)

Try to optimize it to \ (< \ mathrm {o} (n \ log n), \ mathrm {o} (1) > \)

First, the tree is doubled, but only the highest binary bit of \ (k \) is skipped when asking, that is to skip \ (2^{\lfloor \log k \rfloor} \) steps

Chain head preprocessing violence jumps up and down the chain to reach the point

Then jump to the head of the long chain and solve it by preprocessing information

Looking for \ (k \) ancestors can't find any practical problems A search is full of questions that can be done by dsu on tree and tree DP

I hope I can use it in the future

Code :

The template question directly gives the father of each point, so there is no need to add inverse edges, and no value is passed into the father node during dfs

Don't forget to add the opposite side when writing normally

And my template constant is so big

int head[N],ecnt = -1;
struct Edge {
    int nxt,to,w;
}e[N];
inline void AddEdge(int st,int ed) {
    e[++ecnt] = (Edge) {head[st],ed};
    head[st] = ecnt;
}

int dep[N];
int anc[N][20];
int hson[N],len[N],top[N];
std::vector<int> upp[N],low[N];
void dfs1(int u) {
    len[u] = dep[u] = dep[anc[u][0]] + 1;
    fe(i,u) {
        int v = e[i].to;
        anc[v][0] = u;
        for(int j = 0;anc[v][j];++j)
            anc[v][j + 1] = anc[anc[v][j]][j];
        dfs1(v);
        if(len[v] > len[u])
            len[u] = len[v],hson[u] = v;
    }   
}

void dfs2(int u,int t) {
    top[u] = t;
    if(u == t) {
        int p = u;
        ff(i,0,len[u] - dep[u])
            upp[u].push_back(p),p = anc[p][0];
        p = u;
        ff(i,0,len[u] - dep[u])
            low[u].push_back(p),p = hson[p];
    }
    if(hson[u]) dfs2(hson[u],t);
    fe(i,u) {
        int v = e[i].to;
        if(v == hson[u]) continue;
        dfs2(v,v);
    }
}

int l2[N];
inline int query(int u,int k) {
    if(!k) return u;
    u = anc[u][l2[k]];
    k -= (1 << l2[k]),k -= dep[u] - dep[top[u]];
    u = top[u];
    return k >= 0 ? upp[u][k] : low[u][-k];
}

Topics: Graph Theory