【题目大意】
先有一棵树,点i有点权vi,你的任务是找一条简单链,使得链上所有点的点权乘积模上10^6+3 = k。输出这条链的两个端点(u,v),如果有多条,输出字典序最小的。
【思路】
很明显能想到点的分治,现在的关键是,如果根为root,怎么去找不同的子树里的两个点u,v,使得u到v,路径上点的乘积满足题意。我们可以假设u到root,路径上的乘积为x,v到root(不包括root),路径上的乘积为y。如果(u,v)满足题意的路径,那么有 x*y%MOD == k。整理一下,实际上就是 x*y + MOD*t == k,其中,t为一个整数。注意到MOD是一个质数,由扩展gcd知识得:对于确定x,y在[0,MOD)这个区间,只有一个解。当然,如果x == 0的话,y是不止一个解了,但是题目的数据范围,显然x不可能为0。至此,我们感觉已经可以解了,我们分治的时候,直接找对应的y就是了。
但是悲剧的是,此题时间很紧,每次都用扩展gcd去找对应解要超时,对于确定k,先预处理也要超时。我们考虑另外一种解法,因为 y == ( (k - MOD*t)/ x )%MOD,我们可以只预处理一次,将所有x的逆元求出来,那么 y == k*x' %MOD。这样,这题就可以做了。
#pragma comment(linker, "/STACK:102400000,102400000")
#include<cstdio>
#include<cstring>
#include<vector>
#include<queue>
#include<cmath>
#include<cctype>
#include<string>
#include<algorithm>
#include<iostream>
#include<ctime>
#include<map>
#include<set>
using namespace std;
#define MP(x,y) make_pair((x),(y))
#define PB(x) push_back(x)
typedef __int64 LL;
//typedef unsigned __int64 ULL;
/* ****************** */
const int INF=100011122;
const double INFF=1e100;
const double eps=1e-8;
const LL mod=1000000007;
const int NN=100010;
const int MM=1000010;
/* ****************** */
const LL MOD=1000003;
int inv[MOD];
int re[MOD];
struct G
{
int v,next;
}E[NN*2];
int p[NN],T;
int val[NN];
int si[NN],m_si[NN],tol;
bool vis[NN];
int a[NN],mul[NN],b[NN];
pair<int,int> ans;
LL exgcd(LL a,LL &x,LL b,LL &y)
{
if(b==0)
{
x = 1;
y = 0;
return a;
}
LL d = exgcd(b,x,a%b,y);
x = y;
y = (d - a*x)/b;
return d;
}
void init(int k)
{
int i;
LL x0,t;
for(i=1;i<MOD;i++)
{
exgcd(i,x0,MOD,t);
x0*=k;
x0%=MOD;
if(x0<0)x0+=MOD;
inv[i]=x0;
}
}
void add(int u,int v)
{
E[T].v=v;
E[T].next=p[u];
p[u]=T++;
}
void dfs_fr(int u,int fa,LL temp)
{
int i,v;
LL tt = temp*val[u]%MOD;
si[u]=1;
m_si[u]=0;
a[++tol]=u;
mul[tol]=tt;
for(i=p[u];i+1;i=E[i].next)
{
v=E[i].v;
if(v==fa || vis[v])continue;
dfs_fr(v,u,tt);
si[u]+=si[v];
m_si[u]=max(m_si[u],si[v]);
}
}
void goo(int u,int v)
{
if(u>v)swap(u,v);
pair<int,int>temp = MP(u,v);
ans = min(ans,temp);
}
void solve(int root,int kk)
{
int i,j,v,m,t,dui,toll=0;
int new_root=root;
tol=0;
dfs_fr(root,-1,1);
m=m_si[root];
for(i=1;i<=tol;i++)
{
t = max(tol-si[a[i]],m_si[a[i]]);
if(m>t)
{
m=t;
new_root = a[i];
}
}
for(i=p[new_root];i+1;i=E[i].next)
{
v=E[i].v;
if(vis[v])continue;
tol=0;
dfs_fr(v,new_root,1);
for(j=1;j<=tol;j++)
{
if((LL)mul[j]*val[new_root]%MOD==kk)
{
goo(new_root,a[j]);
}
dui = (LL)inv[ mul[j] ]*kk%MOD;
t = re[ dui ];
if( t != INF)
{
goo(t,a[j]);
}
}
for(j=1;j<=tol;j++)
{
t = (LL)mul[j]*val[new_root]%MOD;
re[t] = min(re[t], a[j]);
b[toll++] = t;
}
}
vis[new_root]=true;
for(j=0;j<toll;j++)
re[ b[j] ]=INF;
for(i=p[new_root];i+1;i=E[i].next)
{
v=E[i].v;
if(vis[v])continue;
solve(v,kk);
}
}
inline int read()
{
char ch;
int d, flag = 1;
while( ch= getchar(), ch== ' ' || ch== '\n' );
if( ch== '-' ) { flag= -1; d= 0; }
else{ d= ch- '0'; }
while( ch= getchar(), ch>= '0' && ch<= '9' )
d= (d<<1) + (d<<3) + ch- '0';
return d*flag;
}
int main()
{
int n,k,i,u,v;
init(1);
for(i=0;i<MOD;i++)
re[i]=INF;
while(scanf("%d%d",&n,&k)!=EOF)
{
memset(p,-1,sizeof(p));
T=0;
for(i=1;i<=n;i++)
{
val[i] = read();
}
for(i=1;i<n;i++)
{
u = read();
v = read();
add(u,v);
add(v,u);
}
memset(vis,false,sizeof(vis));
ans = MP(INF,INF);
solve(1,k);
if(ans.first==INF)
puts("No solution");
else
{
// cout<<"ans==";
printf("%d %d\n",ans.first,ans.second);
}
}
return 0;
}