HDU 4812 (点分治)
题目:https://vjudge.net/contest/307753#problem/E
题意:给你一颗树,树上每个点都有个权值,现在问你是否存在 一条路径的乘积 mod 1e6+3 等于 k的路径,如果有找到字典序最小的方案
思路,树上路径~点分治 我们能知道每条路径的值,现在我们可以转化的问题是,怎么找一条路径等于K,和两条路径的乘积等于K, 首先第一种很明显就是判断相不相等即可,第二种的话,我们知道所有路径,我们怎么找到O(n)找到两个呢,我们用个数组存下所有是否出现过,然后,其实就是一个简单的小学问题,我们枚举每个距离的时候相当于 x,y,z已经知道 x,z了,式子是x*y=z,我们就只要判断z/x是否在标记数组中出现过即可,又因为这个有mod ,所以我们只能去乘z的逆元,这个时间卡的有点紧,我加了输入挂,和预处理逆元,map标记都不能用,只能用普通标记数组。
然后还有一个问题,你是否能和之前那样直接求出来所有的距离,答案是否定的,因为你直接去遍历数组标记,数组中的路径还含有两个都是同一子树的情况,这种时候是不能加入标记数组的,但是怎么避免呢,这里用到一个巧妙地方法,我们直接在计算所有路径到重心的距离的时候去更新答案,因为我们只有得到一个子树所有答案的时候才会存入标记数组,这样就避免一个子树的路径发生冲突的情况。最后我们再清空掉我们当前重心存入的答案。
还有更新答案的时候要注意的是,我们前面子树都保存的是点到重心的路径值,这里我们就不能也用点到重心的值了,因为就会多乘了一个重心节点的值,看下图

上面就是两条红色路径相乘就是两个路径合并起来了,主要还是因为这是点权,覆盖路径上所有点的点
#pragma comment(linker,"/STACK:102400000,102400000")
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<iostream>
#include<vector>
#include<map>
#define maxn 1000005
#define mod 1000003
#define MAX 0x3f3f3f
using namespace std;
typedef long long ll;
struct edge{
int to,next;
}e1[*maxn];
ll da;
ll flag[maxn];
//vector<ll> mp[maxn];//存下图
bool vis[maxn];//标记曾经使用过的重心
ll maxsize[maxn],dis[maxn],d[maxn],last[maxn];//maxsize 当前节点的最大子树
ll siz[maxn],e[maxn],e2[maxn],id[maxn],wd[maxn],inv[maxn];// dis 到重心的距离 d 出现过的距离
ll n,m,rt,sum,qe,qe2,ans1,ans2,cnt; // siz 当前节点的子树个数 e 出现的距离 rt代表当前重心
inline ll read()
{
ll x=;char ch=getchar();
while(ch<''||ch>'')ch=getchar();
while(ch>=''&&ch<=''){x=x*+ch-'';ch=getchar();}
return x;
}
void insert(int u,int v)
{
e1[++cnt].to=v;e1[cnt].next=last[u];last[u]=cnt;
e1[++cnt].to=u;e1[cnt].next=last[v];last[v]=cnt;
}
void find(ll x,ll f){//找出重心
siz[x]=;
maxsize[x]=;
for(int i=last[x];i;i=e1[i].next){
ll q=e1[i].to;
if(q==f||vis[q]) continue;//vis数组标记曾经使用过的重心
find(q,x);
siz[x]+=siz[q];
maxsize[x]=max(maxsize[x],siz[q]);
}
maxsize[x]=max(maxsize[x],sum-siz[x]);//节点总数减去当前的子树数=以当前节点为根的父亲点子树数
if(maxsize[x]<maxsize[rt]){
rt=x;
}
}
void query(ll x,ll y){
if(x>y) swap(x,y);
if(x<ans1||(x==ans1&&y<ans2)){
ans1=x;
ans2=y;
}
}
void get_dis(ll x,ll f,ll len,ll root){
ll t=len%mod;
if(t==m){//判断当前路径是否直接等于m
query(root,x);
}
t=t*inv[wd[root]]%mod;//除去重心到子树那段距离,原因就是上述图
ll t1=inv[t]*m%mod;
e[++qe]=len%mod;
e2[++qe2]=len%mod;//后面清空标记
id[qe]=x;
if(flag[t1]){//看是否另一条路径存在
query(flag[t1],x);
}
for(int i=last[x];i;i=e1[i].next){
ll q=e1[i].to;
if(q==f||vis[q]) continue;
// dis[q]=(dis[x]*len)%mod;
get_dis(q,x,(len*wd[q])%mod,root);
}
}
void divide(ll x){
//solve(x,wd[x]);
qe2=;
vis[x]=;
for(int i=last[x];i;i=e1[i].next){
ll q=e1[i].to;
qe=;
get_dis(q,x,wd[x]%mod*wd[q]%mod,x);
for(int i=;i<=qe;i++){//记录当前的子树所有的距离
if(flag[e[i]]==) flag[e[i]]=id[i];
else flag[e[i]]=min(flag[e[i]],id[i]);
}
}
for(int i=;i<=qe2;i++){//清空标记
flag[e2[i]]=;
}
for(int i=last[x];i;i=e1[i].next){
ll q=e1[i].to;
if(vis[q]) continue;
sum=siz[q];
rt=;
maxsize[rt]=MAX;
find(q,x);
divide(rt);
}
}
void init(){
for(int i=;i<=n;i++) last[i]=;
for(int i=;i<=n;i++) vis[i]=;
for(int i=;i<=n;i++) flag[i]=;
}
void pre(){
cnt=;
inv[] = inv[] = ;
for (ll i = ; i < maxn; i++)
inv[i] = (mod - mod / i)*inv[mod%i] % mod;
}
int main(){
pre();
while(scanf("%lld%lld",&n,&m)!=EOF)
{
//if(n==0&&m==0) break;
ll a,b,c;
init();
ans1=MAX;ans2=MAX;
for(int i=;i<=n;i++) wd[i]=read();
for(int i=;i<n;i++)
{
int u=read(),v=read();
insert(u,v);
}
sum=n;//当前节点数
rt=;
maxsize[]=MAX;//置初值
find(,);
divide(rt);
if(ans1!=MAX&&ans2!=MAX) printf("%lld %lld\n",ans1,ans2);
else printf("No solution\n");
}
}
HDU 4812 (点分治)的更多相关文章
- E - D Tree HDU - 4812 点分治+逆元
这道题非常巧妙!!! 我们进行点分治的时候,算出当前子节点的所有子树中的节点,到当前节点节点的儿子节点的距离,如下图意思就是 当前节点的红色节点,我们要求出红色节点的儿子节点绿色节点,所有绿色的子树节 ...
- hdu 4812 DTree (点分治)
D Tree Time Limit: 10000/5000 MS (Java/Others) Memory Limit: 102400/102400 K (Java/Others)Total S ...
- HDU 4812 D Tree
HDU 4812 思路: 点分治 先预处理好1e6 + 3以内到逆元 然后用map 映射以分治点为起点的链的值a 成他的下标 u 然后暴力跑出以分治点儿子为起点的链的值b,然后在map里查找inv[b ...
- hdu 5016 点分治(2014 ACM/ICPC Asia Regional Xi'an Online)
Mart Master II Time Limit: 12000/6000 MS (Java/Others) Memory Limit: 65536/65536 K (Java/Others)T ...
- HDU - 4812 D Tree 点分治
http://acm.hdu.edu.cn/showproblem.php?pid=4812 题意:有一棵树,每个点有一个权值要求找最小的一对点,路径上的乘积mod1e6+3为k 题解:点分治,挨个把 ...
- HDU 4812 D Tree 树分治+逆元处理
D Tree Problem Description There is a skyscraping tree standing on the playground of Nanjing Unive ...
- hdu 4812 D Tree(树的点分治)
D Tree Time Limit: 10000/5000 MS (Java/Others) Memory Limit: 102400/102400 K (Java/Others) Total ...
- HDU 4812 D Tree 树分治
题意: 给出一棵树,每个节点上有个权值.要找到一对字典序最小的点对\((u, v)(u < v)\),使得路径\(u \to v\)上所有节点权值的乘积模\(10^6 + 3\)的值为\(k\) ...
- HDU 4812:D Tree(树上点分治+逆元)
题目链接 题意 给一棵树,每个点上有一个权值,问是否存在一条路径(不能是单个点)上的所有点相乘并对1e6+3取模等于k,输出路径的两个端点.如果存在多组答案,输出字典序小的点对. 思路 首先,(a * ...
随机推荐
- spring4.1.8扩展实战之四:感知spring容器变化(SmartLifecycle接口)
本章是<spring4.1.8扩展实战>的第四篇,如果业务上需要在spring容器启动和关闭的时候做一些操作,可以自定义SmartLifecycle接口的实现类来扩展,本章我们通过先分析再 ...
- windows mysql官方绿色版zip包安装教程
环境: 系统环境 Windows 10 64位 mysql版本 5.7.19 一.万变不离的下载 下载页面:https://dev.mysql.com/downloads/mysql/ 点击 Down ...
- WPF数据模板中绑定事件不触发问题
今天比较闲,做一个练手的项目,结果在xaml中写了一个用户的数据模板后,在其中的某个Canvas上绑定了一个鼠标左击的事件,结果调试的时候,无论怎么点击都不跳到断点那里,百思不得其解. 之后尝试不绑定 ...
- Altium Designer chapter2总结
原理图开发环境这节中需要注意的如下: (1)电路图首先项设定中需注意的地方: 1.General:中经常用到的自动生成交叉节点.放置元件时自动增加选项.复合封装元件的字母数字后缀选项.默认电源对象名称 ...
- Yaconf – 一个高性能的配置管理扩展
鸟哥出品:http://www.laruence.com/2015/06/12/3051.html 首先说说, 这个是干啥的. 我见过很多的项目中, 用PHP文件做配置的, 一个config目录下可能 ...
- java包装类,自动装箱,拆箱,以及基本数据类型与字符串的转换
package cn.learn; import java.util.ArrayList; /* 包装类 java.lang中,基本运算类型效率高 装箱:把基本类型数据包装为包装类 1.构造方法 In ...
- python学习第二十九天函数局部变量如何改变外部变量
python函数局部变量如何改变外部变量,之前我说过,局部变量是没办法改变外部变量的,除非局部变量找不到,去外部找,输出变量,使用关键词global 使变量改变外部变量. 1,使用关键词global ...
- HDU 6315 Naive Operations 【势能线段树】
<题目链接> 题目大意: 给出两个序列,a序列全部初始化为0,b序列为输入值.然后有两种操作,add x y就是把a数组[x,y]区间内全部+1,query x y是查询[x,y]区间内∑ ...
- 攻防世界--Shuffle
测试文件:https://adworld.xctf.org.cn/media/task/attachments/a03353e605bc436798a7cabfb11be073 1.准备 获得信息 3 ...
- Java加密与解密的艺术 读书心得
现在项目中加密与解密的方式很多,很早就想整理一下Java中加密与解密的方式,读完<<Java加密与解密的艺术>>一书.借此机会梳理一下这方面的知识点 一.基础普及 安全技术目标 ...