题目描述

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 10 6 + 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 <= 10 5) and K(0 <=K < 10 6 + 3). The following line contains n numbers v i(1 <= v i < 10 6 + 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")

题目分析

很清晰应该使用树分治,但是不同于一般树分治的是这里要求我们记录两个点的位置。这就意味这不能直接套之前的模板,而必须做出一些变通。

首先,求重心、进行分治应该是没有变化的,变的是对每个重心进行处理的部分。我们要把握住问题的难点在于如果记录答案的话,我们如何记录两个端点以及如何处理记录以后去掉子树中重复的操作

一般的树分治先将以重心为根的路径全部遍历一遍,将所有符合要求的答案保存,然后再在相同参数下遍历子树,将子树中满足条件的(说明刚才重复计算的部分)去掉。最后得到的就是答案。然而我们这里需要记录答案,朴素的想法就是同样将所有符合的都记录下来(同时记录两个端点),然后再在子树中将这些答案删除。可是这种删除将会非常耗费时间,我们必须在那个暂时保存答案的集合里面找到同一对数,最快也得O(nlogn),更何况我们这个操作要进行很多次。肯定会超时,所以我们必须换一个思路思考这个问题。

必须要删除的核心关键在于我们在第一次访问重心的时候没有办法判断是否在同一个子树上,所以只能先加上再减去。有没有方法能够避免计算同一个子树上的答案呢?

我原本的想法是每次访问重心得到其他点到重心的距离前先访问一次重心进行染色,将每个子树染成不同的颜色,然后再处理数据的时候再判断是否在同一个子树上来判断是否是有效数据。可是题目要求记录的是乘积等于k的字典序最小的一对端点。我们这样做就必须记录下所有的端点,答案可能很多,先不说存的下不,仅仅是记录的操作估计都会超时。

不得已我上网看了以下其他人是怎么做的。他们的做法很巧妙,也让我对树分治有了更深刻的理解。

首先,我们必须扭转将所有路径的答案都记录下来再进行处理的观念。这样对于只是求路径个数的可能没有什么问题,但是对于这种需要具体得到两个端点的情况会超时。所以我们必须可以在某一端点处直接判断是否存在有其他端点可以和它凑成答案。为了达到这个效果我们用一个数组T记录以当前重心为根的情况下各个端点距离重心距离x所对应的端点为T[x],因为最后需要的是字典序最小的,所以我们保存所有T[x]中最小的就可以。然后对于每一个端点我们得到它的距离Dis[i]以后判断是否有其他端点到树根的距离为x使得Dis[i]*x==k,为了快速得到这个x,我们需要预处理以下乘法逆元Rev[]。则和端点i对应的端点就应该是T[Rev[Dis[x]]]。如果i和对应的端点都存在则和当前答案比较,确定是否更新答案。

如果不清楚什么是乘法逆元可以看一下我的这篇博客:逆元

可是这算是解决了如何存储两个端点的问题。但是如何消除同一个子树中的端点的影响呢?我们的做法是不再像之前一样一下遍历所有的节点了,而是一个子树一个子树的遍历。并且对T数组的更新放在对答案的判断之后。这样做的原因是访问一个子树的时候,他们这个字数上的信息没有被更新到T数组中,因此T数组中保存的就是前面的子树的信息,也就不会出现访问到同一个子树的情况啦。

在每次开始分治,我们的T数组应该是互相没有联系的,因此需要进行清空

同时我们用一个栈来保存访问子树中所有节点的信息,先更新答案后再更新T数组。

还需要注意一点的是我们的信息是保存在节点上的,因此根节点的信息先不要传递,在更新答案的时候再算上就可以啦。

AC代码

#pragma comment(linker,"/STACK:102400000,102400000")
#include<iostream>
#include<cstring>
#include<cstdio>
#include<climits>
#include<algorithm>
#include<ctime>
#include<cstdlib>
#include<queue>
#include<set>
#include<map>
#include<cmath>#define RG register
#define IL inlineusing namespace std;
typedef long long ll;
const int MAXN=2e5+5;
const int MOD=1e6+3;
struct edge
{int to,last;
}Edge[MAXN<<2]; int Last[MAXN],tot;
int n,kk,SonNum[MAXN],MaxNum[MAXN],Vis[MAXN];
int root,rootx,dlen,ss,len;
ll Dis[MAXN],Rev[MOD+5],Num[MAXN];
int Stack[MAXN],T[MOD+5]; int top;
int ansl,ansr;IL int getint()
{int x=0,sign=1; char c=getchar();while(c<'0' || c>'9'){if(c=='-') sign=-1; c=getchar();}while(c>='0' && c<='9'){x=(x<<1)+(x<<3)+c-'0'; c=getchar();}return x*sign;
}
IL ll getll()
{ll x=0,sign=1; char c=getchar();while(c<'0' || c>'9'){if(c=='-') sign=-1; c=getchar();}while(c>='0' && c<='9'){x=(x<<1)+(x<<3)+c-'0'; c=getchar();}return x*sign;
}IL void Init()
{for(int i=0;i<=n;++i) Last[i]=0,Vis[i]=false;tot=0; ansl=ansr=INT_MAX;
}IL void AddEdge(int u,int v)
{Edge[++tot].to=v; Edge[tot].last=Last[u]; Last[u]=tot;
}IL void Read()
{for(RG int i=1;i<=n;++i) Num[i]=getll();int u,v;for(RG int i=1;i<n;i++){u=getint(); v=getint();AddEdge(u,v); AddEdge(v,u);}
}void GetRoot(int x,int father)
{int v;SonNum[x]=1; MaxNum[x]=1;for(int i=Last[x];i;i=Edge[i].last){v=Edge[i].to; if(v==father || Vis[v]) continue;GetRoot(v,x);SonNum[x]+=SonNum[v];if(SonNum[v]>MaxNum[x]) MaxNum[x]=SonNum[x];}MaxNum[x]=max(MaxNum[x],ss-SonNum[x]);if(rootx>MaxNum[x]) root=x,rootx=MaxNum[x];
}void GetDis(int x,int father,ll now)
{ll t=now*Num[x]%MOD;Dis[x]=t; Stack[top++]=x;int v;for(RG int i=Last[x];i;i=Edge[i].last){v=Edge[i].to; if(v==father|| Vis[v]) continue;GetDis(v,x,t);}
}void Update(ll x,ll y,int z)
{int tmp=T[Rev[x*y%MOD]*kk%MOD];if(!tmp) return;if(z>tmp) swap(z,tmp);if(z<ansl || z==ansl&&tmp<ansr) ansl=z,ansr=tmp;
}void Solve(int x)
{//memset(Dis,0,sizeof(Dis));int v;T[1]=x;Vis[x]=true;for(RG int i=Last[x];i;i=Edge[i].last){dlen=0; top=0;v=Edge[i].to; if(Vis[v]) continue;GetDis(v,x,1);for(RG int j=0;j<top;++j) Update(Num[x],Dis[Stack[j]],Stack[j]);for(RG int j=0;j<top;++j) if(T[Dis[Stack[j]]]==0 || T[Dis[Stack[j]]]>Stack[j]) T[Dis[Stack[j]]]=Stack[j];}for(int i=Last[x];i;i=Edge[i].last){dlen=0; top=0;v=Edge[i].to; if(Vis[v]) continue;GetDis(v,x,1);for(int j=0;j<top;++j) T[Dis[Stack[j]]]=0;}for(int i=Last[x];i;i=Edge[i].last){v=Edge[i].to; if(Vis[v]) continue;//ans-=Count(v,Deal(x,0));ss=SonNum[v]; rootx=INT_MAX; root=0;GetRoot(v,x);Solve(root);}
}IL void Work()
{rootx=INT_MAX; ss=n; root=0; GetRoot(1,0); Solve(root);
}IL void Write()
{if(ansl==INT_MAX && ansr==INT_MAX){printf("No solution\n");}else{printf("%d %d\n",ansl,ansr);}
}IL void Pre()
{Rev[1]=1;for(ll i=2;i<MOD;++i)Rev[i]=(MOD-MOD/i)*Rev[MOD%i]%MOD;
}int main()
{Pre();while(~scanf("%d%d",&n,&kk)){Init();Read();Work();Write();}return 0;
}

经验总结

对于一个问题首先要抽象出来需要用那种算法进行解决。局部的处理常常需要其他算法和数据结构的优化。尽可能不适用mapset等,能用数组还是尽量用数组。对数组的初始化也需要讲究技巧,不能简单使用一个memset,这种做法很容易超时。

HDU4812-D Tree-树分治相关推荐

  1. POJ 1741 Tree 树分治

    题意: 给出一颗有\(n (n \leq 10^4)\)个节点的树,和一个\(k\).统计有多少个点对\(u, \, v(u \neq v)\)满足\(u\)到\(v\)的最短距离不超过\(k\). ...

  2. 点分治问题 ----------- HDU4812 D Tree [点分治 + 乘法逆元]

    题目链接 题目大意: 给你一颗树,树上节点有个权值aia_iai​,现在问你这颗树上是否存在一条路径这个路径上面权值的乘积模1e6+3等于k. 如果有多组答案输出字典序最小的答案 解题思路: 1.首先 ...

  3. POJ 1741 Tree(树分治)

    去网上搜题解大多数都是说论文,搜了论文看了看,写的确实挺好,直接复制过来. 不过代码中有些细节还是要注意的,参考这篇http://blog.sina.com.cn/s/blog_6d5aa19a010 ...

  4. POJ1741 Tree(树分治——点分治)题解

    题意:给一棵树,问你最多能找到几个组合(u,v),使得两点距离不超过k. 思路:点分治,复杂度O(nlogn*logn).看了半天还是有点模糊. 显然,所有满足要求的组合,连接这两个点,他们必然经过他 ...

  5. Tree(树分治入门)

    题目链接:http://poj.org/problem?id=1741 Tree Time Limit: 1000MS   Memory Limit: 30000K Total Submissions ...

  6. POJ1741:Tree——题解+树分治简要讲解

    http://poj.org/problem?id=1741 题目大意:给一棵树,求点对间距离<=k的个数. -------------------- 以这道题为例记录一下对于树分治的理解. 树 ...

  7. POJ1741 Tree 树中点对统计【树分治入门】

    算法合集之<分治算法在树的路径问题中的应用> 论文下载地址 树被定义为没有圈的连通图,有几个性质 在树中去掉一条边后,得到的图是不连通的 在树中添加一条边后,一定存在一条边 树的每一对顶点 ...

  8. 【楼天城男人八题】【树分治|Treap+启发式合并】POJ1741 Tree

    题面在这里 待我先膜拜一下楼教主-- 首先这题是很明显的树分治 想说点什么却发现已经没什么好说了 然后我们来看另一种解法:平衡树乱搞 这里用的是Treap实现 对于每个节点,用Treap记录该子树每个 ...

  9. CodeForces - 1217F Forced Online Queries Problem(线段树分治+并查集撤销)

    题目链接:点击查看 题目大意:给出 nnn 个点,初始时互相不存在连边,需要执行 mmm 次操作,每次操作分为两种类型: 1xy1 \ x \ y1 x y:如果 (x,y)(x,y)(x,y) 之间 ...

  10. 牛客多校8 - All-Star Game(线段树分治+并查集按秩合并的撤销操作)

    题目链接:点击查看 题目大意:有 n 个球员和 m 个球迷,一个球员可能是多个球迷的粉丝,需要选择最少的球员进行比赛,使得所有的球迷都愿意观看(对于每个球迷来说,都有至少一个其喜欢的球员入选比赛) 对 ...

最新文章

  1. no qualifying bean of type_就是要让你彻底学会 @Bean 注解
  2. 2022年全球及中国户外电源产品行业容量前景与运营动态分析报告
  3. talking web android,Talking Web
  4. 基于token与基于服务器的身份认证
  5. 使用管控策略,设定多账号组织全局访问边界
  6. Java学习笔记之log4j与commons-logging转
  7. 函数声明和函数表达式
  8. 帆软报表插件开发之fine-decision中的ControllerRegisterProvider扩展
  9. 我的世界装mod要下java_【新人必看教程】我的世界HMCL下载安装mod模组
  10. Linux下安装Java环境
  11. JSON和API接口初识
  12. 写给喜欢单片机的初学者
  13. adb shell 小米手机_【ADB命令实战】免ROOT停用小米手机系统应用
  14. CSP共空间模式详解
  15. MyBatis Mapper.xml的choose/case标签详解
  16. nc6单据模板设置没有可选的模板问题解决
  17. Jupyter notebook无法执行代码
  18. 余世维 - 老板首先要诚实
  19. 信道容量的迭代算法实现
  20. 在这个产品同质化比较严重的时代,这个局怎么破?

热门文章

  1. So easy Webservice 1.Socket建设web服务
  2. Ubuntu下将Sublime Text设置为默认编辑器
  3. 步骤菜单使用css3实现
  4. vba将select的值直接赋给变量
  5. 测试Rockey 4 Smart加密锁的C语言代码
  6. WCF学习(五)数据契约之已知类型
  7. JavaScript 参考教程——写在前面
  8. 11计算机,11-计算机科学与技术
  9. html一张图片用两种滤镜,HTML图片CSS滤镜—灰度效果
  10. 华为大数求和 java_大数乘积java