hdu 4812 树分治

这种题目很不擅长,接下来要多加训练

#include <iostream>
#include <algorithm>
#include <cstdio>
#include <string>
#include <cstring>
#include <cmath>
#include <vector>
#include <list>
#include <map>
#include <set>
#include <deque>
#include <queue>
#include <stack>
#include <bitset>
#include <functional>
#include <sstream>
#include <iomanip>
#include <cmath>
#include <cstdlib>
#include <ctime>
#pragma comment(linker, "/STACK:102400000,102400000")
typedef long long ll;
#define INF 1e9
#define MAXN 10000
#define MAXM 100
const int maxn = 100005;
const int mod = 1000003;
#define eps 1e-6
#define pi 3.1415926535897932384626433
#define rep(i,n) for(int i=0;i<n;i++)
#define rep1(i,n) for(int i=1;i<=n;i++)
#define scan(n) scanf("%d",&n)
#define scanll(n) scanf("%I64d",&n)
#define scan2(n,m) scanf("%d%d",&n,&m)
#define scans(s) scanf("%s",s);
#define ini(a) memset(a,0,sizeof(a))
#define out(n) printf("%d\n",n)
//ll gcd(ll a,ll b) { return b==0?a:gcd(b,a%b);}
using namespace std;
vector<int> G[maxn];
int h[mod+10]; //记录积为i的重心
int pro[maxn]; // 记录当前到i号点的积
int vex[mod+10]; // 记录积为i的点
int inv[mod+10]; //记录i的逆
int id[maxn]; //记录该次dfs产生的点和积
int ans[2]; //记录当前答案
int son[maxn]; //记录以i当前为根的结点个数
int wroot,wval;//当前重心编号,以及以邻居为根的最大点数的最小值(描述有点乱,详细的看代码
bool vis[maxn]; //描述是否访问过该节点(只有重心才会被标记)
int val[maxn];
int cnt;
int n, K;
void init()
{
	ini(vis);
	ini(son);
	ans[0] = ans[1] = INF;
	ini(h);
}
void getRoot(int u,int fa) //求u所在“连通块”的重心,一遍dfs,复杂度O(N)
{
	son[u] = 1;
	int mxv = 0;
	for(int i = 0;i < G[u].size(); i++)
	{
		int v = G[u][i];
		if(vis[v] || v == fa) continue;
		getRoot(v,u);
		son[u] += son[v];
		mxv = max(mxv, son[v]);
	}

	mxv = max(mxv,son[0] - son[u]);
	if(wval > mxv)
	{
		wroot = u;
		wval = mxv;
	}
}
void dfs(int u,int fa,int Prep)
{
	int tmp = pro[cnt] = (ll)Prep * val[u] % mod; //一直wa在这,一定要加tmp,若放dfs(pro[cnt-1])会出错
	id[cnt++] = u;
	son[u] = 1;
	for(int i = 0;i < G[u].size(); i++)
	{
		int v = G[u][i];
		if(vis[v] || v == fa) continue;
		dfs(v,u,tmp);
		son[u] += son[v];
	}
}
void update(int a,int b)
{
	if(a > b) swap(a,b);
	if(a < ans[0]) ans[0] = a, ans[1] = b;
	else if(a == ans[0] && b < ans[1]) ans[1] = b;
}
void solve(int u,int all)
{
	//cout<<u<<' '<<all<<endl;
	if(all == 1) return; 
	son[0] = all;
	wval = INF;
	getRoot(u,-1);

	u = wroot;
	//	cout<<u<<endl;
	for(int i = 0;i < G[u].size(); i++)
	{
		int v = G[u][i];
		if(vis[v] || v == u) continue;
		cnt = 0;
		dfs(v,u,val[u]);
		rep(i,cnt)
		{
			//	cout<<pro[i]<<endl;
			if(pro[i] == K) update(u,id[i]);
			int x = (ll)pro[i] * inv[val[u]] % mod; //这条链的积
			int y = (ll)K * inv[x] % mod; // K/x部分
			if(h[y] == u)
			{
				update(id[i],vex[y]);
			}
		}
		rep(i,cnt) //将一条条链的积保存下来
		{
			if(h[pro[i]] == u)
			{
				vex[pro[i]] = min(id[i],vex[pro[i]]);
			}
			else
			{
				h[pro[i]] = u;
				vex[pro[i]] = id[i];
			}
		}
	}
	vis[u] = 1; //标记u
	for(int i = 0;i < G[u].size(); i++)
	{
		int v = G[u][i];
		if(vis[v] || v == u) continue;
		solve(v,son[v]);
	}
}
ll ext_gcd(ll a,ll b,ll &x,ll &y)
{
	if(b==0)
	{
		x=1;y=0;
		return a;
	}
	ll d=ext_gcd(b,a%b,y,x);
	y-=a/b*x;
	return d;
}
int main()
{
#ifndef ONLINE_JUDGE  
	freopen("in.txt","r",stdin);  
	//   freopen("out.txt","w",stdout);  
#endif 
	ll x,y;
	for(int i=0;i<mod;++i)
	{
		ext_gcd(i,mod,x,y);
		x%=mod;
		if(x<0) x+=mod;
		inv[i]=x;
	}
	while(~scanf("%d%d",&n,&K))
	{
		init();
		rep1(i,n){
			scanf("%d",&val[i]);
			G[i].clear();
		}
		int x,y;
		rep(i,n-1)
		{
			scan2(x,y);
			G[x].push_back(y);
			G[y].push_back(x);
		}
		solve(1,n);
		if(ans[0] == INF) puts("No solution");
		else printf("%d %d\n",ans[0],ans[1]);
	}

	return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值