Multi school sprint NOIP simulation 27 - Super doubling - reconstruction tree

Keywords: Algorithm data structure Graph Theory

No link is provided for this question

Title Description

Given a tree. We think one x → y x\rightarrow y Simple path of x → y( x ≠ y x\neq y x  = y) is good if and only if the point number on the path is the smallest x x x. The biggest is y y y.

Find the number of good simple paths and the size of the tree n ≤ 2 × 1 0 6 n\le 2\times 10^6 n≤2×106.

preface

I had a terrible headache this morning. It hurts when I eat, walk, read and think about questions. In the middle of the game, I got down and had a rest. As soon as I got down, I got down for nearly an hour. The problem was confused. It was found that it was false halfway through the divide and conquer. It was not easy to think of the correct solution later.

The last question could have cheated more points.

Now I just pray that the headache can be cured before NOIP.

Problem solution

First, there is a failed routine point divide and conquer method. The statistical path is a two-dimensional partial order, which can be achieved if it is well written O ( n log ⁡ 2 n ) O(n\log^2 n) O(nlog2n), but it can't be achieved by any optimization O ( n log ⁡ n ) O(n\log n) O(nlogn), not to mention that this problem requires a small constant.

But what can I do without some divide and conquer? DP or set of data structures directly on the tree cannot be achieved at all O ( n 2 ) O(n^2) Below O(n2).

We shuffle the conditions and find that we only need to meet the point number on the path is not less than x x x and not greater than y y y. And all numbers are greater than or equal to x x Point and of x x x The most common block composed of x is unique, x x x to y y The point number on the path of y is not less than x x x. If and only if y y y is in this connected block. If we call this connecting block x x If the suffix connected block of x is defined in a similar way, the original condition can be equivalent converted to x x x in y y The prefix of y is in the connected block, and y y y in x x The suffix of x is in the connected block.

The rest about how to get this connected block is another routine method, which we use similar methods K r u s k a l \rm Kruskal A point reconstruction tree is established by Kruskal reconstruction tree. K r u s k a l \rm Kruskal Kruskal reconstruction tree starts with a pile of scattered points, and then continuously connects the connected blocks at both ends of the edge to the same new father (the mouth here ignores many details). Then the reconstruction number of this point is that there is no point at first, and then add a new point and take all the connected blocks connected to it as their own son.

Two reconstruction trees can be built through two different enumeration sequences. The two connected blocks corresponding to a point are the subtrees of the point on the two reconstruction trees. The two reconstruction trees are divided into A A A, B B B. Then we just need to x x x. Statistics A A How many points are in the subtree of A B B Ancestors in B. Yes A A A first find the DFS order, convert the subtree into an interval, and then directly B B For DFS on B, use the data structure to maintain a single point and modify the interval to sum.

Single point modified interval summation can just be done with tree array, so the total complexity n log ⁡ n n\log n nlogn, the constant is very small.

code

#include<cstdio>//JZM yyds!!
#include<cstring>
#include<iostream>
#include<algorithm>
#include<cmath>
#include<ctime>
#include<vector>
#include<queue>
#include<stack>
#include<map>
#include<set>
#define ll long long
#define uns unsigned
#define MOD 
#define MAXN 2000005
#define INF 1e17
#define IF (it->first)
#define IS (it->second)
using namespace std;
inline ll read(){
	ll x=0;bool f=1;char s=getchar();
	while((s<'0'||s>'9')&&s>0)f^=(s=='-'),s=getchar();
	while(s>='0'&&s<='9')x=(x<<1)+(x<<3)+(s^48),s=getchar();
	return f?x:-x;
}
int pt[30],lp;
inline void print(ll x,char c='\n'){
	if(x<0)putchar('-'),x=-x;
	pt[lp=1]=x%10;
	while(x>9)x/=10,pt[++lp]=x%10;
	while(lp)putchar(pt[lp--]^48);
	putchar(c);
}
inline ll lowbit(ll x){return x&-x;}
struct edge{
	int v,to;edge(){}
	edge(int V,int T){v=V,to=T;}
}e[MAXN<<2];
int EN,G[MAXN];
inline void addedge(int u,int v){
	e[++EN]=edge(v,G[u]),G[u]=EN;
	e[++EN]=edge(u,G[v]),G[v]=EN;
}
int n,fa[MAXN];
ll ans;
int G1[MAXN],G2[MAXN];
inline int finds(int x){
	return !fa[x]?x:(fa[x]=finds(fa[x]));
}
inline void addedge1(int u,int v){
	e[++EN]=edge(v,G1[u]),G1[u]=EN;
}
inline void addedge2(int u,int v){
	e[++EN]=edge(v,G2[u]),G2[u]=EN;
}
int hd[MAXN],tl[MAXN],IN,f[MAXN];
inline void add(int x,int d){
	for(;x<=n;x+=lowbit(x))f[x]+=d;
}
inline int sum(int x){
	int res=0;
	for(;x>0;x^=lowbit(x))res+=f[x];
	return res;
}
inline void pdfs(int x){
	hd[x]=++IN;
	for(int i=G2[x];i;i=e[i].to)pdfs(e[i].v);
	tl[x]=IN;
}
inline void dfs(int x){
	ans+=sum(tl[x])-sum(hd[x]-1);
	add(hd[x],1);
	for(int i=G1[x];i;i=e[i].to)dfs(e[i].v);
	add(hd[x],-1);
}
signed main()
{
	freopen("charity.in","r",stdin);
	freopen("charity.out","w",stdout);
	n=read(),read();
	for(int i=2;i<=n;i++)addedge(read(),i);
	for(int x=1;x<=n;x++)
		for(int i=G[x];i;i=e[i].to){
			int v=e[i].v,fv;
			if(v<x&&((fv=finds(v))^x))
				addedge1(x,fv),fa[fv]=x;
		}
	for(int i=1;i<=n;i++)fa[i]=0;
	for(int x=n;x>0;x--)
		for(int i=G[x];i;i=e[i].to){
			int v=e[i].v,fv;
			if(v>x&&((fv=finds(v))^x))
				addedge2(x,fv),fa[fv]=x;
		}
	pdfs(1),dfs(n);
	print(ans);
	return 0;
}

Posted by phpnewbie911 on Wed, 10 Nov 2021 08:11:15 -0800