【点分治】VW's Contest #1 C

本文介绍了一种使用点分治策略解决特定类型问题的方法。通过每次选择链中最大值最小的节点进行处理,确保了每一步操作的有效性。该算法利用递归和数据结构实现了高效的求解过程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

分析:

考场上没来得及看

其实也不难

点分治水水就过了

每次加入链最大值最小的,这样每次加入时,能够保证它是所有已加入点的最大值。

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<queue>
#include<vector>
#define SF scanf
#define PF printf
#define MAXN 100010
#define MAXP 10000010
using namespace std;
typedef long long ll;
vector<int> a[MAXN];
int n,P;
ll ans;
bool used[MAXN];
int siz[MAXN],siz1[MAXN];
void dfs(int x,int fa=0){
	siz[x]=1;
	for(int i=0;i<int(a[x].size());i++){
		int u=a[x][i];
		if(u==fa||used[u])
			continue;
		dfs(u,x);
		siz[x]+=siz[u];
	}
}
int get_siz(int x,int siztot,int fa=0){
	int g=0;
	siz1[x]=siztot-siz[x];
	for(int i=0;i<int(a[x].size());i++){
		int u=a[x][i];
		if(u==fa||used[u])
			continue;
		int g1=get_siz(u,siztot,x);
		siz1[x]=max(siz1[x],siz[u]);
		if(g==0||siz1[g]>siz1[g1])
			g=g1;
	}
	if(g==0||siz1[g]>siz1[x])
		g=x;
	return g;
}
int find_g(int x){
	dfs(x);	
	return get_siz(x,siz[x]);
}
struct node{
	int u,maxv,fa,sumv;
	node () {}
	node (int u1,int maxv1,int sumv1,int fa1):u(u1),maxv(maxv1),sumv(sumv1),fa(fa1) {}
	bool operator <(const node &a) const{
		return maxv>a.maxv;
	}
};
priority_queue<node>q;
int val[MAXN],totv[MAXP];
int st[MAXN],tp;
int calc(int x,int fa=0){
	int g=x;
	if(fa==0)
		q.push(node(x,val[x],val[x]%P,0));
	else
		q.push(node(x,max(val[x],val[fa]),(val[x]+val[fa])%P,fa));
	if(fa)
		g=fa;
	ll res=0;
	while(!q.empty()){
		node newp=q.top();
		q.pop();
		int x=newp.u;
		res+=totv[(newp.maxv-newp.sumv+P+val[g])%P];
//		PF("{%d %d %d %d}\n",x,totv[(newp.maxv-newp.sumv+P+val[g])%P],newp.maxv,newp.sumv);
		totv[newp.sumv]++;
		st[++tp]=newp.sumv;
		for(int i=0;i<int(a[x].size());i++){
			int u=a[x][i];
			if(u==newp.fa||used[u])
				continue;
			q.push(node(u,max(newp.maxv,val[u]),(newp.sumv+val[u])%P,x));	
		}
	}
	while(tp)
		totv[st[tp--]]--;
	return res;
}
void solve(int x){
	int g=find_g(x);	
//	PF("[%d]\n",g);
	used[g]=1;
	ans+=calc(g);
	ans++;
//	PF("[%d %lld]\n",g,ans);
	for(int i=0;i<int(a[g].size());i++){
		int u=a[g][i];
		if(used[u])
			continue;
		ans-=calc(u,g);		
	}
//	PF("<%d %lld>\n",g,ans);
	for(int i=0;i<int(a[g].size());i++){
		int u=a[g][i];
		if(used[u])
			continue;
		solve(u);
	}
}
int main(){
	freopen("c.in","r",stdin);
	freopen("c.out","w",stdout);
	SF("%d%d",&n,&P);
	int u,v;
	for(int i=1;i<n;i++){
		SF("%d%d",&u,&v);	
		a[u].push_back(v);
		a[v].push_back(u);
	}
	for(int i=1;i<=n;i++){
		SF("%d",&val[i]);
	}
	solve(1);
	PF("%lld",ans);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值