HDU P4812 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 branch can be treated as a vertex). Today the students under the tree are considering a problem: Can we find such a chain on the tree so that the multiplication of all integers on the chain (mod 106 + 3) equals to K?
Can you help them in solving this problem?
Input
There are several test cases, please process till EOF.
Each test case starts with a line containing two integers N(1 <= N <= 105) and K(0 <=K < 106 + 3). The following line contains n numbers vi(1 <= vi < 106 + 3), where vi indicates the integer on vertex i. Then follows N - 1 lines. Each line contains two integers x and y, representing an undirected edge between vertex x and vertex y.
Output
For each test case, print a single line containing two integers a and b (where a < b), representing the two endpoints of the chain. If multiply solutions exist, please print the lexicographically smallest one. In case no solution exists, print “No solution”(without quotes) instead.
For more information, please refer to the Sample Output below.
Sample 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
Sample Output
3 4
No solution
Hint
“please print the lexicographically smallest one.”是指: 先按照第一个数字的大小进行比较,若第一个数字大小相同,则按照第二个数字大小进行比较,依次类推。
若出现栈溢出,推荐使用C++语言提交,并通过以下方式扩栈:
#pragma comment(linker,"/STACK:102400000,102400000")
题目大意
给出多组数据
每组数据有一个
n
和
然后给出
对于每组数据输出字典序最小且满足两点间路径积在模意义下为 k <script type="math/tex" id="MathJax-Element-14">k</script> 的点对(包含这两个点,两个点可重叠)
若无解则输出“No solution”
题解
点分治
详解写在代码里
代码
#include<cstdio>
#include<cstring>
#define LL long long
#define maxn 100005
#define p 1000003
#define INF 0x7fffffff
using namespace std;
int n,k,tot,ans1,ans2,rt,top;
int lnk[maxn],sz[maxn],maxe[maxn],id[maxn];
LL d[maxn],val[maxn],tmp[maxn],inv[p+2],map[p+2];
bool vis[maxn];
struct edge
{
int nxt,y;
} e[maxn<<1];
//tmp,id为栈,tmp记录dfs中出现的点到根节点所经过路径上点权的积(根节点不一定是重心),id记录dfs中出现的点的序号
//map[i]记录以当前重心为一个端点时,路径上点权积为i的另一端的序号
int readln()
{
int x=0;
char ch=getchar();
while (ch<'0'||ch>'9') ch=getchar();
while ('0'<=ch&&ch<='9') x=x*10+ch-48,ch=getchar();
return x;
}
int max(int x,int y){return x>y?x:y;}
int min(int x,int y){return x<y?x:y;}
void swap(int &x,int &y){int t=x;x=y;y=t;}
void add(int x,int y)
{
tot++;e[tot].nxt=lnk[x];lnk[x]=tot;e[tot].y=y;
tot++;e[tot].nxt=lnk[y];lnk[y]=tot;e[tot].y=x;
}
void getrt(int x,int fa) //树形DP求重心
{
sz[x]=1;maxe[x]=0;
for (int i=lnk[x];i;i=e[i].nxt)
{
int y=e[i].y;
if (y==fa||vis[y]) continue;
getrt(y,x);
sz[x]+=sz[y];
maxe[x]=max(maxe[x],sz[y]);
}
maxe[x]=max(maxe[x],n-sz[x]);
if (maxe[rt]>maxe[x]) rt=x;
}
void dfs(int x,int fa)//遍历子节点,入栈并求出到根节点路径上的点权积
{
tmp[++top]=d[x];id[top]=x;
for (int i=lnk[x];i;i=e[i].nxt)
{
int y=e[i].y;
if (y==fa||vis[y]) continue;
d[y]=(d[x]*val[y])%p;
dfs(y,x);
}
}
void query(int x,int id) //求字典序最小且满足条件的点对
{
x=inv[x]*k%p;
int y=map[x];
if (y==0) return;
if (y>id) swap(y,id);
if (y<ans1||(y==ans1&&id<ans2)) ans1=y,ans2=id;
}
void solve(int x)
{
int y;
vis[x]=true;
map[val[x]]=x;
for (int i=lnk[x];i;i=e[i].nxt)
{
y=e[i].y;
if (vis[y]) continue;
top=0;d[y]=val[y];
dfs(y,x);
for (int j=1;j<=top;j++) query(tmp[j],id[j]);
//计算以子节点y为端点但不经过x的路径上所经过的点权积,以方便和以x为端点但不包含y的节点的路径进行合并形成一条经过重心x的路径
top=0;d[y]=(val[x]*val[y])%p;
dfs(y,x);
for (int j=1;j<=top;j++)
{
y=map[tmp[j]];
if (!y||id[j]<y) map[tmp[j]]=id[j];
}
//计算以x为端点且包含y的节点的路径,与原来map[i]所对应的序号进行比较
}
map[val[x]]=0;
for (int i=lnk[x];i;i=e[i].nxt)
{
y=e[i].y;
if (vis[y]) continue;
top=0;d[y]=(val[x]*val[y])%p;
dfs(y,x);
for (int j=1;j<=top;j++) map[tmp[j]]=0;
}
for (int i=lnk[x];i;i=e[i].nxt)//删去当前重心,寻找剩余子树的重心,继续进行上述操作
{
y=e[i].y;
if (vis[y]) continue;
rt=0;n=sz[y];
getrt(y,0);solve(rt);
}
}
int main()
{
inv[1]=1;
for (int i=2;i<p;i++) inv[i]=(inv[p%i]*(p-p/i)+p)%p;
//递推求逆元,不懂得话可以看我博客里面有一篇关于数学定理证明的blog
while (~scanf("%d%d",&n,&k))
{
memset(vis,false,sizeof(vis));
memset(lnk,0,sizeof(lnk));
tot=0;ans1=ans2=INF;
for (int i=1;i<=n;i++) val[i]=readln();
for (int i=1;i<n;i++) add(readln(),readln());
rt=0;maxe[0]=INF;
getrt(1,0);solve(rt);
if (ans1==INF) printf("No solution\n"); else printf("%d %d\n",ans1,ans2);
}
return 0;
}