题解 | D-第四次忍界大战(点分治做法)

第四次忍界大战

https://ac.nowcoder.com/acm/contest/112575/D

点分治?

点分治适合处理大规模的树上路径信息问题。

点分治具体来说就是将该问题分为路径经过root的路径,以及路径不经过root的路径。显然,路径经过root的是不难处理的,你从root进入,然后dfs就行了。那么不经过root的路径那?不慌,你不经过root,经不经过以root为根的子树的子树的重心?还不经过?那么继续递归。

当然点分治做法需要注意的细节还是比较多的。因为还是涉及多个函数,还都是递归,这个调起来会有亿点难度。

这边提供一组在调试的时候试出来的hack数据,是一条链。

10
4 4 5 3 2 5 4 4 5 5
5 10
1 3
5 6
4 6
9 2
1 7
8 9
2 3
10 7

ans:
6

图大概是长这样的。 alt

那么最终的算法也不需要特判两个点的情况,特别的舒适。

前面WA了好多发,最后靠hack数据调出来了的就是根节点的处理。

那么前面我们把根节点的A值当路径上的A值传入(第4行dfs最后一个参数传入A[u]),这个其实会导致根节点情况无法被统计到。

		// 保证计入答案的路径是经过u点的
		for(auto &v:g[u]){
			if(vis[v]) continue;	// 你不能就不判断了
			dfs(dfs,v,u,INF);
			for(auto &a:wp){
				// 跨区域合并不行,因为根节点值太低了
				if(a>=A[u]) continue;
				if(cnt.count(a)){
					ans+=cnt[a];
				}
			}
			for(auto &a:wp){
				++cnt[a];
			}

			wp.clear();
		}
		// 根节点直接和子树上的点匹配
		ans+=cnt[A[u]];

AC代码

https://ac.nowcoder.com/acm/contest/view-submission?submissionId=78175127

// Problem: 第四次忍界大战
// Contest: NowCoder
// URL: https://ac.nowcoder.com/acm/contest/112575/D
// Memory Limit: 512 MB
// Time Limit: 2000 ms
// 
// Powered by CP Editor (https://cpeditor.org)

#include <bits/stdc++.h>
#define all(vec) vec.begin(),vec.end()
#define pb push_back
#define SZ(a) ((int) a.size())
#define FOR(i, a, b) for (int i = (a); i <= (b); ++i)
#define ROF(i, a, b) for (int i = (a); i >= (b); --i)
#define debug(var) cerr << #var <<":"<<var<<"\n";
#define lson(var) (var<<1)
#define rson(var) ((var<<1)+1)

using namespace std;

using  ll = long long;using ull = unsigned long long;
using DB=double;using LD=long double;
// using i128 = __int128;
using pdd = pair<DB,DB>;using plb = pair<ll,bool>;
using pll = pair<ll,ll>;
using arr3 = array<ll,3> ;using arr2 = array<ll,2>;
constexpr ll MAXN=static_cast<ll>(1e6)+10,INF=static_cast<ll>(1e17)+9; // 1e18+9也是素数
constexpr ll MAXM=(ll)1e6+10;constexpr ll MAXV=(ll)1e5+10;
constexpr ll mod=static_cast<ll>(1e9)+7;
constexpr double eps=1e-8;const double pi=acos(-1.0);

ll N,M,Q,X,K,T,lT,A[MAXN];
/*

*/
inline void solve(){
	cin>>N;
	vector<vector<ll> > g(N+5);
	FOR(i,1,N){
		cin>>A[i];
	}
	FOR(i,1,N-1){
		ll u,v;
		cin>>u>>v;
		g[u].pb(v);g[v].pb(u);
	}
	vector<ll> sz(N+5,0);ll mxTree=INF;
	vector<bool> vis(N+5,0);ll root=0;
	ll ans=0;
	// FOR(i,1,N){		// 特判两个点的情况
		// for(auto &v:g[i]){
			// if(A[v]==A[i]) ++ans;
		// }
	// }
	// ans/=2;
// #ifdef LOCAL
    // debug(ans);
// #endif
	auto get=[&](auto &&self,ll u,ll p,ll tot)->void{
		ll val=0;sz[u]=1;
		for(auto &v:g[u]){
			if(v==p || vis[v]) continue;
			self(self,v,u,tot);
			sz[u]+=sz[v];
			val=max(val,sz[v]);
		}
		val=max(val,tot-sz[u]);
		if(val<mxTree){
			mxTree=val;
			root=u;
		}
	};
	auto cal=[&](ll u)->void{
		map<ll,ll> cnt;vector<ll> wp;
		// 那么众所周知,我们只考虑经过u的路径(点分治)
		auto dfs=[&](auto &&self,ll u,ll p,ll mn)->void{
			if(mn>A[u]){
				// if(cnt.count(A[u])){
					// ans+=cnt[A[u]];
				// }
				// ++cnt[A[u]];
				wp.pb(A[u]);
			}
			mn=min(A[u],mn);
			for(auto &v:g[u]){
				if(v==p || vis[v]) continue;
				self(self,v,u,mn);
			}
		};
#ifdef LOCAL
	debug(root);
   	debug(ans);
#endif
		// 保证计入答案的路径是经过u点的
		for(auto &v:g[u]){
			if(vis[v]) continue;	// 你不能就不判断了
			dfs(dfs,v,u,INF);
// #ifdef LOCAL
    // if(root==1){
    	// for(auto &a:wp){
    		// debug(a);
    	// }
    	// debug(ans);
    // }
// #endif
			for(auto &a:wp){
				// 跨区域合并不行,因为根节点值太低了
				if(a>=A[u]) continue;
				if(cnt.count(a)){
					ans+=cnt[a];
				}
			}
			for(auto &a:wp){
				++cnt[a];
			}

			wp.clear();
		}
		// 根节点直接和子树上的点匹配
		ans+=cnt[A[u]];
	};
	// 主程序
	auto dfz=[&](auto &&self,ll u)->void{
		vis[u]=true;	// u一定是一个重心
		cal(u);
		for(auto &v:g[u]){
			if(vis[v]) continue;
			mxTree=INF;
			get(get,v,u,sz[v]);
// #ifdef LOCAL
    // debug(root);
// #endif
			self(self,root);
		}
	};
	get(get,1,1,N);
	dfz(dfz,root);
	ans*=2;		// 该路径是考虑顺序的
	cout<<ans<<"\n";
}

int main()
{
	ios::sync_with_stdio(false);
	cin.tie(0);cout.tie(0);
	
	// cin>>lT;
	// while(lT--)
		solve();
	return 0;
}
/*
WA 60% https://ac.nowcoder.com/acm/contest/view-submission?submissionId=78164958
hack:
10
4 4 5 3 2 5 4 4 5 5
5 10
1 3
5 6
4 6
9 2
1 7
8 9
2 3
10 7

ans:
6
AC https://ac.nowcoder.com/acm/contest/view-submission?submissionId=78175127
*/

全部评论

相关推荐

一表renzha:手写数字识别就是一个作业而已
点赞 评论 收藏
分享
评论
2
收藏
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务