HDU P4812 D Tree

6 篇文章 0 订阅

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

  1. “please print the lexicographically smallest one.”是指: 先按照第一个数字的大小进行比较,若第一个数字大小相同,则按照第二个数字大小进行比较,依次类推。

  2. 若出现栈溢出,推荐使用C++语言提交,并通过以下方式扩栈:

#pragma comment(linker,"/STACK:102400000,102400000")

题目大意

给出多组数据

每组数据有一个 n k 表示树的大小以及所求路径点权积在模意义下的值为 k

然后给出 n 个点的权值以及 n -1 条边

对于每组数据输出字典序最小且满足两点间路径积在模意义下为 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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值