题目大意
给出一棵树,让你寻找一条路径,使得路径上的点相乘mod10^6+3等于k,输出路径的两个端点,按照字典序最小输出。
input
5 60
2 5 2 3 3
1 2
1 3
2 4
2 5
5 2
2 5 2 3 3
1 2
1 3
2 4
2 5
output
3 4
No solution
idea
这个题像是换了一种思想。
直接用个图来讲吧
![]()
紫色的是每个节点的权值,粉红色的是节点编号,k等于120
我们开一个set,用来存当前已有的值,命名为s
首先我们遍历1的子树,我们走到2号节点,去s里面找与120 / (5*3)发现没有,继续走5号节点,找120 / (4*3 5),发现没有,又走到6号节点,找120 / (3*5*3)发现没有,返回2号节点,这个时候这棵子树就遍历完了,我们把5,4*5,5*3丢进s。继续去遍历4,这个时候,我们找120/(3 4)发现没有,找120/(3 * 4 * 3)发现没有,返回到3号节点,把4, 4 * 3丢进s,这个时候我们走到了4号节点这个位置,去找120 /(3 * 2), 发现有,ok记录此时的编号,继续放下走,去找120/(3 * 2 * 1),发现没有, 继续放下走,去找120/(3 * 2 * 2),发现没有。返回4号节点,把2, 2 * 1, 2 * 2丢进去。1号节点子树结束 ,递归枚举其余子树。
code
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 1e6 + 100;
const int mod = 1e6 + 3;
int root, sonMax[maxn], sonNum[maxn], allNode, p[maxn];
ll k, data[maxn], inv[maxn];
int ans1, ans2;
bool vis[maxn];
set<ll> s;
set<ll> ::iterator it;
vector<int> Edge[maxn];
//inv用来存放逆元
//data是每个节点的值
//p是当前值所对应的节点编号
void Get_inv() { //线性处理逆元
inv[0] = 0, inv[1] = 1;
for(int i = 2; i < maxn; i++) {
inv[i] = (mod - mod / i) % mod * inv[mod % i];
inv[i] %= mod;
}
}
void getroot(int now, int pre) {
sonMax[now] = 0; sonNum[now] = 1;
for(int i = 0; i < Edge[now].size(); i++) {
int to = Edge[now][i];
if(to == pre || vis[to]) continue;
getroot(to, now);
sonNum[now] += sonNum[to];
sonMax[now] = max(sonMax[now], sonNum[to]);
}
sonMax[now] = max(sonMax[now], allNode - sonNum[now]);
if(sonMax[root] > sonMax[now]) root = now;
}
void check_node (int now, int pre, ll val) {
ll t = k * inv[val] % mod; //t是与当前点相对应的数
it = s.find(t);
if(it != s.end()) { //有值,就更新答案
int start = min(p[t], now), End = max(p[t], now);
if(!ans1) ans1 = start, ans2 = End;
else {
if(ans1 > start || (ans1 == start && ans2 > End))
{
ans1 = start; ans2 = End;
}
}
}
for(int i = 0; i < Edge[now].size(); i++) {
int to = Edge[now][i], w = data[to];
if(to == pre || vis[to]) continue;
check_node(to, now, val * w % mod); //继续递归子树
}
}
void push_in(int now, int pre, ll val) {
it = s.find(val); //找当前值是否已经存在
if(it != s.end()) { //存在的话就更新下最小编号
p[val] = min(p[val], now);
}
else s.insert(val), p[val] = now; //否则就直接插入
for(int i = 0; i < Edge[now].size(); i++) {
int to = Edge[now][i], w = data[to];
if(to == pre || vis[to]) continue;
push_in(to, now, val * w % mod);
}
}
void FindNode(int now) {
s.clear();
s.insert(1); //s用来存放现有的值 这里很重要 想一想为什么要把1也放进去
p[1] = now;
for(int i = 0; i < Edge[now].size(); i++) {
int to = Edge[now][i];
if(vis[to]) continue;
check_node(to, now, data[to] * data[now] % mod); //用来遍历这个子树,看这个子树的每个值能否在之前
//遍历过得点找到相对应的点满足要求
push_in(to, now, data[to]); //将这棵子树里的点放进去
}
}
void solve(int now) {
FindNode(now); //处理以这个点为根的子树
vis[now] = true;
for(int i = 0; i < Edge[now].size(); i++) {
int to = Edge[now][i];
if(vis[to]) continue;
sonMax[0] = allNode = sonNum[to];
getroot(to, root = 0);
solve(root);
}
}
int main()
{
int n;
Get_inv(); //预处理逆元
while(~scanf("%d %lld", &n, &k)) {
for(int i = 0; i <= n; i++) {
vis[i] = false;
Edge[i].clear();
}
for(int i = 1; i <= n; i++)
scanf("%lld", &data[i]);
for(int i = 0; i < n - 1; i++) {
int u, v;
scanf("%d %d", &u, &v);
Edge[u].push_back(v);
Edge[v].push_back(u);
}
ans1 = ans2 = 0; //ans1代表编号小的那个端点 ans2表示这条路的另一个端点
sonMax[0] = allNode = n;
getroot(1, root = 0);
solve(root);
if(!ans1) printf("No solution\n");
else printf("%d %d\n", ans1, ans2);
}
}