preface
After thinking about the positive solution for a long time, we can only use the points we just learned yesterday to divide and conquer violence qwq.
subject
\(\rm 2s\ 512MB.\)
\(\ tt PPL \) is tired of random walk in \ (\ rm ZBQC \), and now he wants to try some good paths.
For well-known reasons, \ (\ rm ZBQC \) is very small. There is only one path between two small points, that is, it can be regarded as a tree.
\(\ tt PPL \) thinks that Haokang's path \ (x\rightarrow y \) should satisfy that \ (x \) is the point with the smallest number on the path and \ (Y \) is the point with the largest number on the path.
\(\ tt PPL \) after you decide how many good health paths you tell him, go through all these paths. Can you tell him?
\(1\le n\le 2\times 10^6.\)
explain
Now it's a random part. If you want to see the positive solution directly, please slide down.
Buddha, I've been doing some divide and conquer in recent days. When I see this problem, I want to point divide and conquer. Finally, I tried to \ (O(n) \) solve the two-dimensional partial order. I can only Hu a \ (O(n\log_2^2n) \) violence and get \ (60pts. \)
I can't wait to talk about my point division.
First, divide and conquer directly, and then consider how to calculate the contribution of the path of \ (u \). Consider handling the following two paths with \ (u \) as one end and \ (v \) as the other end:
- \(v \) is the maximum value for this path.
- \(v \) is the minimum value of this path.
Single chain is a good answer. Here we talk about how to spell the two chains to calculate the answer.
If we have the first kind of path \ (a \) and the second kind of path \ (B \), they can spell it together if and only if \ (a_v \) is the maximum of the two paths and \ (b_v \) is the minimum of the two paths.
We record their maximum and minimum values for both paths, then they should satisfy this relationship: \ (B {min} < a {min} < a {Max} < B {Max}. \)
But in fact, we only consider the two restrictions \ (B {min} < a {min} \) and \ (a {Max} < B {Max} \), because other restrictions must be met.
Then it is found that this is a two-dimensional partial order. Directly sort + tree array. Note that those in the same subtree cannot be counted, so they should be subtracted. The total time complexity is \ (O(n\log_2^2n) \).
Yes, just like This article As mentioned, I try to solve two-dimensional partial order in linear time, so the complexity is \ (O(n\log_2n) \), ha ha!
Put up a copy of violence to prove that I'm not talking nonsense//12252024832524 #include <bits/stdc++.h> #define TT template<typename T> using namespace std; typedef long long LL; const int MAXN = 2000005; int n; LL ans; LL Read() { LL x = 0,f = 1; char c = getchar(); while(c > '9' || c < '0'){if(c == '-') f = -1;c = getchar();} while(c >= '0' && c <= '9'){x = (x*10) + (c^48);c = getchar();} return x * f; } TT void Put1(T x) { if(x > 9) Put1(x/10); putchar(x%10^48); } TT void Put(T x,char c = -1) { if(x < 0) putchar('-'),x = -x; Put1(x); if(c >= 0) putchar(c); } TT T Max(T x,T y){return x > y ? x : y;} TT T Min(T x,T y){return x < y ? x : y;} TT T Abs(T x){return x < 0 ? -x : x;} int head[MAXN],tot; struct edge { int v,nxt; }e[MAXN<<1]; void Add_Edge(int x,int y) { e[++tot] = edge{y,head[x]}; head[x] = tot; } void Add_Double_Edge(int x,int y) { Add_Edge(x,y); Add_Edge(y,x); } bool vis[MAXN]; int MAX[MAXN],siz[MAXN],rt; void getrt(int x,int fa,int S) { siz[x] = 1; MAX[x] = 0; for(int i = head[x],v; i ;i = e[i].nxt) { if((v = e[i].v) == fa || vis[v]) continue; getrt(v,x,S); siz[x] += siz[v]; MAX[x] = Max(MAX[x],siz[v]); } MAX[x] = Max(MAX[x],S-siz[x]); if(MAX[x] < MAX[rt] || !rt) rt = x; } void predfs(int x,int fa) { siz[x] = 1; for(int i = head[x],v; i ;i = e[i].nxt) { if((v = e[i].v) == fa || vis[v]) continue; predfs(v,x); siz[x] += siz[v]; } } int B[MAXN]; int lowbit(int x){return x & -x;} void Add(int x,int val){for(int i = x;i <= n;i += lowbit(i)) B[i] += val;} int Sum(int x){int ret = 0;for(int i = x;i >= 1;i -= lowbit(i)) ret += B[i];return ret;} int mat,mit; struct node { int x,re; }ma[MAXN],mi[MAXN]; vector<node> fkma[MAXN],fkmi[MAXN]; void getm(int x,int fa,int MIN,int MAX,int cao) { if(x == MIN) mi[++mit] = node{x,MAX},fkmi[cao].emplace_back(node{x,MAX}); if(x == MAX) ma[++mat] = node{x,MIN},fkma[cao].emplace_back(node{x,MIN}); for(int i = head[x],v; i ;i = e[i].nxt) if(!vis[v = e[i].v] && v != fa) getm(v,x,Min(MIN,v),Max(MAX,v),cao); } void solve(int x) { // printf("solve11 %d %lld\n",x,ans); mat = mit = 0; for(int i = head[x],v; i ;i = e[i].nxt) if(!vis[v = e[i].v]) getm(v,x,Min(x,v),Max(x,v),v); for(int i = 1;i <= mit;++ i) if(mi[i].re == x) ++ans; for(int i = 1;i <= mat;++ i) if(ma[i].re == x) ++ans; if(!mat || !mit) { for(int i = head[x],v; i ;i = e[i].nxt) if(!vis[v = e[i].v]) fkmi[v].clear(),fkma[v].clear(); return; } for(int i = head[x],v; i ;i = e[i].nxt) if(!vis[v = e[i].v]) { if(!fkmi[v].size() || !fkma[v].size()) continue; sort(fkma[v].begin(),fkma[v].end(),[](node A,node B){ return A.x < B.x; }); sort(fkmi[v].begin(),fkmi[v].end(),[](node A,node B){ return A.re < B.re; }); int now = 0,lenmi = fkmi[v].size(); for(int i = 0,lenma = fkma[v].size();i < lenma;++ i) { while(now < lenmi && fkmi[v][now].re < fkma[v][i].x) Add(fkmi[v][now].x,1),++now; if(now > 0) ans -= Sum(fkma[v][i].re-1); } for(int i = 0;i < now;++ i) Add(fkmi[v][i].x,-1); fkmi[v].clear(); fkma[v].clear(); } sort(ma,ma+mat+1,[](node A,node B){ return A.x < B.x; }); sort(mi,mi+mit+1,[](node A,node B){ return A.re < B.re; }); int now = 1; for(int i = 1;i <= mat;++ i) { while(now <= mit && mi[now].re < ma[i].x) Add(mi[now].x,1),++now; if(now > 1) ans += Sum(ma[i].re-1); } for(int i = 1;i < now;++ i) Add(mi[i].x,-1); // printf("solve22 %d %lld\n",x,ans); } void dfs(int x) { vis[x] = 1; predfs(x,0); solve(x); for(int i = head[x],v; i ;i = e[i].nxt) { if(vis[v = e[i].v]) continue; rt = 0; getrt(v,x,siz[v]); dfs(rt); } } int main() { freopen("charity.in","r",stdin); freopen("charity.out","w",stdout); n = Read(); Read(); for(int i = 2;i <= n;++ i) Add_Double_Edge(Read(),i); rt = 0; getrt(1,0,n); dfs(rt); Put(ans,'\n'); return 0; } /* 6 0 1 2 2 3 5 12 */
Next is the positive solution.
The solution is wonderful, I can't think of it.
What can you think of considering the maximum value of the path\ (\ tt Kruskal \) rebuild the tree! This problem is point weight, so we can build a special refactoring tree.
We need to build two reconstruction trees to ensure that \ (lca(x,y) \) is the maximum / minimum value on the \ (x \ rightarrow, y \) path. This process can be realized by adding edges in reverse order of the union search set.
Then our question is how many point pairs are ancestor descendant relationships in one tree and descendant ancestor relationships in another tree.
In fact, it is also a partial order problem. You can directly use the tree array.
The total time complexity \ (O(n\log_2n) \) is minimal.
code
The classical positive solution is shorter than violence//12252024832524 #include <bits/stdc++.h> #define TT template<typename T> using namespace std; typedef long long LL; const int MAXN = 2000005; int n; LL ans; LL Read() { LL x = 0,f = 1; char c = getchar(); while(c > '9' || c < '0'){if(c == '-') f = -1;c = getchar();} while(c >= '0' && c <= '9'){x = (x*10) + (c^48);c = getchar();} return x * f; } TT void Put1(T x) { if(x > 9) Put1(x/10); putchar(x%10^48); } TT void Put(T x,char c = -1) { if(x < 0) putchar('-'),x = -x; Put1(x); if(c >= 0) putchar(c); } TT T Max(T x,T y){return x > y ? x : y;} TT T Min(T x,T y){return x < y ? x : y;} TT T Abs(T x){return x < 0 ? -x : x;} int head[3][MAXN],tot; struct edge { int v,nxt; }e[MAXN<<2]; void Add_Edge(int opt,int x,int y) { e[++tot] = edge{y,head[opt][x]}; head[opt][x] = tot; } void Add_Double_Edge(int x,int y) { Add_Edge(0,x,y); Add_Edge(0,y,x); } int f[MAXN]; int findSet(int x){if(f[x]^x)f[x]=findSet(f[x]);return f[x];} int B[MAXN]; int lowbit(int x){return x&-x;} void Add(int x,int val){for(int i = x;i <= n;i += lowbit(i)) B[i] += val;} int Sum(int x){int ret = 0;for(int i = x;i >= 1;i -= lowbit(i)) ret += B[i];return ret;} int dfn[MAXN],dfntot,siz[MAXN]; void dfs1(int x) { dfn[x] = ++dfntot; siz[x] = 1; for(int i = head[1][x]; i ;i = e[i].nxt) dfs1(e[i].v),siz[x] += siz[e[i].v]; } void dfs2(int x) { ans += Sum(dfn[x]+siz[x]-1) - Sum(dfn[x]-1); Add(dfn[x],1); for(int i = head[2][x]; i ;i = e[i].nxt) dfs2(e[i].v); Add(dfn[x],-1); } int main() { freopen("charity.in","r",stdin); freopen("charity.out","w",stdout); n = Read(); Read(); for(int i = 2;i <= n;++ i) Add_Double_Edge(Read(),i); for(int i = 1;i <= n;++ i) f[i] = i; for(int x = 1;x <= n;++ x) for(int i = head[0][x]; i ;i = e[i].nxt) if(e[i].v < x && (x^findSet(e[i].v))) Add_Edge(1,x,findSet(e[i].v)),f[findSet(e[i].v)] = x; for(int i = 1;i <= n;++ i) f[i] = i; for(int x = n;x >= 1;-- x) for(int i = head[0][x]; i ;i = e[i].nxt) if(e[i].v > x && (x^findSet(e[i].v))) Add_Edge(2,x,findSet(e[i].v)),f[findSet(e[i].v)] = x; dfs1(n); dfs2(1); Put(ans,'\n'); return 0; }