• 来点好康的


    前言

    想了巨大久的正解,最后还是只能胡个昨天刚学的点分治暴力qwq。

    题目

    ( m 2s 512MB.)

    ( t PPL)( m ZBQC) 随机游走累了,现在他想尝试一些好康的路径。

    由于众所周知的原因,( m ZBQC) 非常小,小的两点之间有且仅有一条路径,也就是说可以视为一棵树。

    ( t PPL) 认为好康的路径 (x ightarrow y) 应该满足 (x) 是路径上编号最小的点,(y) 是路径上编号最大的点。

    ( t PPL) 决定你告诉他有多少条好康的路径后就把这些路径全部走一遍,你能告诉他吗?

    (1le nle 2 imes 10^6.)

    讲解

    现在是乱扯部分,如果要直接看正解请往下滑。

    佛了,最近几天做点分治人麻了,看到这道题就想点分治,最后试图 (O(n)) 解决二维偏序无果,只能胡了个 (O(nlog_2^2n)) 的暴力,拿到了 (60pts.)

    我已经迫不及待想讲我的点分治做法了。

    首先直接点分治,然后考虑过 (u) 的路径咋算贡献。考虑处理出一下两种以 (u) 为一端,(v) 为另外一端的路径:

    • (v) 是这条路径的最大值。
    • (v) 是这条路径的最小值。

    单链很好算答案,这里我们讲一讲怎么把两条链拼起来算答案。

    假如我们有第一类路径 (a) 和第二类路径 (b),他们能拼起来当且仅当 (a_v) 是两条路径的最大值,(b_v) 是两条路径的最小值。

    我们对这两种路径都记录一下它们的最大值和最小值,那么它们应该满足这个关系:(b_{min}<a_{min}<a_{max}<b_{max}.)

    但事实上我们只用考虑 (b_{min}<a_{min})(a_{max}<b_{max}) 这两个限制,因为其它限制一定满足。

    然后发现这就是二维偏序,直接排序+树状数组搞定,注意在同一棵子树内的不能算,所以要减掉。总时间复杂度为 (O(nlog_2^2n))

    没错,正如这篇文章所提到的,我试图在线性时间内解决二维偏序,这样复杂度就是 (O(nlog_2n)) 了,哈哈!

    挂一份暴力以证明我没在胡扯
    //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
    ",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
    ",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,'
    ');
    	return 0;
    }
    /*
    6
    0 1 2 2 3 5 
    12
    */
    

    接下来是正解。

    解法很奇妙,我想不到。

    考虑路径的最值能想到什么?( t Kruskal) 重构树!这道题是点权,所以我们可以建特殊的重构树。

    我们需要建两棵重构树,保证 (lca(x,y))(x ightarrow y) 路径上的最大值/最小值,这个过程可以用并查集逆序加边实现。

    然后我们的问题就转化为有多少个点对在一棵树上是祖先-后代关系而在另一棵树上是后代-祖先关系。

    其实也是一个偏序问题,直接树状数组即可。

    总时间复杂度 (O(nlog_2n)),常数极小。

    代码

    经典正解比暴力短
    //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,'
    ');
    	return 0;
    }
    
  • 相关阅读:
    (转)音频降噪算法 附完整C代码
    (转)移动直播技术秒开优化经验(含PPT)
    React实现简单的SearchBox搜索框组件
    为ARM安卓设备交叉编译C/C++语言程序
    Ubuntu无法用快捷键或图标打开终端
    Java基础系列-SPI你认识吗
    Java基础系列-时间日期API
    Java基础系列-RandomAccess
    Java基础系列-Optional
    那些字段适不适合建索引?
  • 原文地址:https://www.cnblogs.com/PPLPPL/p/15535872.html
Copyright © 2020-2023  润新知