HDU 4812 D Tree 树分治+逆元+hash新姿势

题意:

给定n个点的树 K

下面n个数是点权

下面n-1行给出树边。

问:

是否存在一条路径使得路径上点权积 % mod  = K

若存在则输出路径的两端。

若存在多条路径则输出字典序最小的一条。

思路:

按树重心分治。

分成路径是否经过树重心。

然后用力码。。

has[x] = u;

表示乘积为x 对应的点是u

但这样has就不能用计数器来优化清空。

所以用2个数组: has[x] = cnt; has_id[x] = u;

这样has里存的是乘积为x是否存在。has_id[x] 来记录点。

#pragma comment(linker, "/STACK:10240000000000,10240000000000")
#include <cstdio>
#include <cstring>
#include <queue>
#include <cmath>
#include <map>
#include <algorithm>
using namespace std;
const int inf = 10000000;
typedef pair<int,int> pii;
typedef long long ll;
const int mod = 1000000+3;
int ni[mod];
void quick(int x){
	int ans = 1, tmp = x, y = mod-2;
	while(y){
		if(y&1)
			ans = (ll)ans * (ll)x % (ll)mod;
		x = (ll)x*(ll)x%(ll)mod;
		y>>=1;
	}
	ni[tmp] = ans;
}
template <class T>
inline bool rd(T &ret) {
	char c; int sgn;
	if(c=getchar(),c==EOF) return 0;
	while(c!='-'&&(c<'0'||c>'9')) c=getchar();
	sgn=(c=='-')?-1:1;
	ret=(c=='-')?0:(c-'0');
	while(c=getchar(),c>='0'&&c<='9') ret=ret*10+(c-'0');
	ret*=sgn;
	return 1;
}
template <class T>
inline void pt(T x) {
	if (x <0) {
		putchar('-');
		x = -x;
	}
	if(x>9) pt(x/10);
	putchar(x%10+'0');
}
#define N 100005
pii ans;
void updata(int x, int y){
	if(x>y)swap(x,y);
	if(ans.first > x || ans.first==x && ans.second > y)
		ans = pii(x,y);
}
struct Edge{
	int to, nex;
}edge[N<<1];
int head[N], edgenum;
void init(){memset(head,-1,sizeof head); edgenum = 0;}
void add(int u, int v){
	Edge E = {v,head[u]};
	edge[edgenum] = E;
	head[u] = edgenum++;
}
int a[N], K, mul[N];
int n, siz[N];
int s[N], id[N], top;
int  has[mod], has_id[mod], tim;
bool vis[N];
void add_hash(int u, int multi_ans){
	if(has[multi_ans] == tim)
		has_id[multi_ans] = min(has_id[multi_ans], u);
	else {
		has[multi_ans] = tim;
		has_id[multi_ans] = u;
	}
}
int wval, wroot;
void getroot(int u, int fa){
	siz[u] = 1;
	int maxv = 0;
	for(int i = head[u]; ~i; i = edge[i].nex){
		int v = edge[i].to; if(v == fa || vis[v])continue;
		getroot(v, u);
		siz[u] += siz[v];
		maxv = max(maxv, siz[v]);
	}
	maxv = max(maxv, siz[0] - siz[u]);
	if(maxv < wval)wval = maxv, wroot = u;
}
int find_root(int u, int fa){ //找到子树的重心
	wval = 1000000;
	getroot(u, fa);
	return wroot;
}

void multi(int u, int fa){
	mul[u] = ((ll)a[u]*(ll)mul[fa])%(ll)mod;
	siz[u] = 1;
	s[top] = mul[u];	id[top++] = u;
	for(int i = head[u];~i;i=edge[i].nex){
		int v = edge[i].to; if(v == fa || vis[v])continue;
		multi(v, u);
		siz[u] += siz[v];
	}
}
void solve(int u){
	tim++;
	mul[u] = 1;
	vis[u] = 1;
	for(int i = head[u]; ~i; i = edge[i].nex){
		int v = edge[i].to; if(vis[v])continue;
		top = 0;
		multi(v, u);
		for(int i = 0; i < top; i++)
		{
			if(K == (ll)s[i]*(ll)a[u]%(ll)mod)
				updata(u, id[i]);
			int x = (ll)K * (ll)ni[ (ll)s[i] * (ll)a[u] % (ll)mod ] % (ll)mod;
			if(has[x] == tim)
				updata(id[i], has_id[x]);
		}
		for(int i = 0; i < top; i++)
		{
			add_hash(id[i], s[i]);
		}
	}
	for(int i = head[u];~i;i=edge[i].nex){
		int v = edge[i].to; if(vis[v])continue;
		siz[0] = siz[v];
		solve(find_root(v, u));
	}
}
void input(){
	init();
	for(int i = 1; i <= n; i++)rd(a[i]);
	for(int i = 1, u, v; i < n; i++)
	{
		rd(u); rd(v);
		add(u,v); add(v,u);
	}
}
int main() {
	tim = 0;
	memset(has, 0, sizeof has);
	for(int i = 0; i < mod; i++) quick(i);
	while(rd(n) && rd(K)) {
		input();
		ans.first = inf;
		memset(vis, 0, sizeof vis);
		siz[0] = n;
		solve(find_root(1,1));
		if(ans.first == inf)puts("No solution");
		else {
			pt(ans.first); putchar(' ');
			pt(ans.second); putchar('\n');
		}
	}
	return 0;
}
时间: 2024-10-17 23:45:17

HDU 4812 D Tree 树分治+逆元+hash新姿势的相关文章

HDU 4812 D Tree 树分治+逆元处理

D Tree Problem Description There is a skyscraping tree standing on the playground of Nanjing University of Science and Technology. On each branch of the tree is an integer (The tree can be treated as a connected graph with N vertices, while each bran

HDU 4812 D Tree 树分区+逆+hash新位置

意甲冠军: 特定n点树 K 以下n号码是正确的点 以下n-1行给出了树的侧. 问: 所以,如果有在正确的道路点图的路径 % mod  = K 如果输出路径的两端存在. 多条路径则输出字典序最小的一条. 思路: 按树重心分治. 分成路径是否经过树重心. 然后用力码. . has[x] = u; 表示乘积为x 相应的点是u 但这样has就不能用计数器来优化清空. 所以用2个数组: has[x] = cnt; has_id[x] = u; 这样has里存的是乘积为x是否存在.has_id[x] 来记录

hdu 4812 D Tree(树的点分治)

D Tree Time Limit: 10000/5000 MS (Java/Others)    Memory Limit: 102400/102400 K (Java/Others) Total Submission(s): 1687    Accepted Submission(s): 263 Problem Description There is a skyscraping tree standing on the playground of Nanjing University of

HDU 3333 Turing Tree 树状数组 离线查询

题意: 给你一个数列,然后有n个查询,问你给定区间中不同数字的和是多少. 思路还是比较难想的,起码对于蒟蒻我来说. 将区间按照先右端点,后左端点从小到大排序之后,对于每个查询,我只要维护每个数字出现的最后一次就可以了(这个结论稍微想一下就可以证明是正确的). 然后就是简单的点更新,区间求和问题了- #include <cstdio> #include <cstring> #include <iostream> #include <map> #include

HDU 4812 D Tree

Description There is a skyscraping tree standing on the playground of Nanjing University of Science and Technology. On each branch of the tree is an integer (The tree can be treated as a connected graph with N vertices, while each branch can be treat

poj 1744 tree 树分治

Tree Time Limit: 1000MS   Memory Limit: 30000K       Description Give a tree with n vertices,each edge has a length(positive integer less than 1001). Define dist(u,v)=The min distance between node u and v. Give an integer k,for every pair (u,v) of ve

【BZOJ-1468】Tree 树分治

1468: Tree Time Limit: 10 Sec  Memory Limit: 64 MBSubmit: 1025  Solved: 534[Submit][Status][Discuss] Description 给你一棵TREE,以及这棵树上边的距离.问有多少对点它们两者间的距离小于等于K Input N(n<=40000) 接下来n-1行边描述管道,按照题目中写的输入 接下来是k Output 一行,有多少对点之间的距离小于等于k Sample Input 7 1 6 13 6

【POJ1741】Tree 树分治 模板咯?

广告: #include <stdio.h> int main() { puts("转载请注明出处[vmurder]谢谢"); puts("网址:blog.csdn.net/vmurder/article/details/44302921"); } 题意: 给你一棵无根树,求有多少点对之间距离<=K. 题解: 树分治. 然后对于一个重心X,我们把它的所有子树中的所有点存到结构体数组中. 结构体中存距离和子树编号. 第一遍sort,我们双指针扫哪些点

hdu 3333 Turing Tree (树状数组+离线处理+离散化)

Turing Tree Time Limit: 6000/3000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total Submission(s): 3981    Accepted Submission(s): 1349 Problem Description After inventing Turing Tree, 3xian always felt boring when solving problems a