HDU 4812:D Tree(树上点分治+逆元)
题意
给一棵树,每个点上有一个权值,问是否存在一条路径(不能是单个点)上的所有点相乘并对1e6+3取模等于k,输出路径的两个端点。如果存在多组答案,输出字典序小的点对。
思路
首先,(a * b) % MOD = k,知道a和k,求b,可以使用逆元来求,于是可以想到用一个类似于map的东西(我这里的Hash数组,记录值为i的时候它的最小下标是多少)存路径长度为b的时候,那个端点是哪个点。
但是我一开始是想着先全部处理好,然后再O(MOD)判一遍,但是发现这种做法的话在有删除的情况下难以解决。
于是要考虑一边更新一边删除。对于当前的根结点的不同子树,分开处理。先判断再更新(因为这样才可以使得路径的两个端点在不同的子树上),最后做完这棵树后要删除。
学习到了逆元打表的写法:
inv[1] = 1LL;
for(LL i = 2; i < MOD; i++)
inv[i] = inv[MOD % i] * (MOD - MOD / i) % MOD;
#include <bits/stdc++.h>
using namespace std;
const int N = 1e5 + 10;
const int MOD = 1e6 + 3;
const int INF = 0x3f3f3f3f;
typedef long long LL;
struct Edge {
int v, nxt;
} edge[N*2];
LL dis[N], w[N], dep[N], inv[MOD + 11];
int n, k, head[N], tot, root, sum, son[N], vis[N], f[N], ans1, ans2;
int Hash[MOD + 11], id[MOD + 11];
void Add(int u, int v) {
edge[tot] = (Edge) { v, head[u] }; head[u] = tot++;
edge[tot] = (Edge) { u, head[v] }; head[v] = tot++;
}
void getroot(int u, int fa) {
son[u] = 1; f[u] = 0;
for(int i = head[u]; ~i; i = edge[i].nxt) {
int v = edge[i].v;
if(vis[v] || fa == v) continue;
getroot(v, u);
son[u] += son[v];
f[u] = max(f[u], son[v]);
}
f[u] = max(f[u], sum - son[u]);
if(f[u] < f[root]) root = u;
}
void getdeep(int u, int fa) {
dep[++dep[0]] = dis[u] % MOD; id[dep[0]] = u;
for(int i = head[u]; ~i; i = edge[i].nxt) {
int v = edge[i].v;
if(vis[v] || fa == v) continue;
dis[v] = dis[u] * w[v] % MOD;
getdeep(v, u);
}
}
void update(LL now, int x) {
now = inv[now] * k % MOD;
int y = Hash[now];
if(y == INF) return ;
if(x > y) swap(x, y);
if(x < ans1 || (x == ans1 && y < ans2)) ans1 = x, ans2 = y;
}
int cal(int u, int st) {
if(st) {
Hash[w[u]] = u;
for(int i = head[u]; ~i; i = edge[i].nxt) {
int v = edge[i].v;
if(vis[v]) continue;
// 判断是否存在答案
dep[0] = 0;
dis[v] = w[v] % MOD;
getdeep(v, u);
for(int j = 1; j <= dep[0]; j++)
update(dep[j], id[j]);
// 更新Hash表
dep[0] = 0;
dis[v] = w[u] * w[v] % MOD;
getdeep(v, u);
for(int j = 1; j <= dep[0]; j++)
Hash[dep[j]] = min(Hash[dep[j]], id[j]);
}
} else {
// 删除操作
Hash[w[u]] = INF;
for(int i = head[u]; ~i; i = edge[i].nxt) {
int v = edge[i].v;
if(vis[v]) continue;
dep[0] = 0;
dis[v] = w[u] * w[v] % MOD;
getdeep(v, u);
for(int j = 1; j <= dep[0]; j++)
Hash[dep[j]] = INF;
}
}
}
void work(int u) {
vis[u] = 1;
cal(u, 1);
cal(u, 0);
for(int i = head[u]; ~i; i = edge[i].nxt) {
int v = edge[i].v;
if(vis[v]) continue;
sum = son[v];
getroot(v, root = 0);
work(root);
}
}
LL f_pow(LL a, int b) {
LL ans = 1;
while(b) {
if(b & 1) ans = ans * a % MOD;
a = a * a % MOD;
b >>= 1;
} return ans % MOD;
}
int main() {
// for(LL i = 0; i < MOD; i++) inv[i] = f_pow(i, MOD - 2);
inv[1] = 1LL;
for(LL i = 2; i < MOD; i++)
inv[i] = inv[MOD % i] * (MOD - MOD / i) % MOD;
while(~scanf("%d%d", &n, &k)) {
memset(head, -1, sizeof(head));
memset(Hash, INF, sizeof(Hash));
memset(vis, 0, sizeof(vis));
tot = 0;
for(int i = 1; i <= n; i++) scanf("%lld", &w[i]);
for(int i = 1; i < n; i++) {
int u, v; scanf("%d%d", &u, &v);
Add(u, v);
}
sum = n, root = 0, f[0] = ans1 = ans2 = INF;
getroot(1, 0);
work(root);
if(ans1 == INF || ans2 == INF) puts("No solution");
else printf("%d %d\n", ans1, ans2);
}
return 0;
}
/*
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
*/
HDU 4812:D Tree(树上点分治+逆元)的更多相关文章
- HDU 4812 D Tree
HDU 4812 思路: 点分治 先预处理好1e6 + 3以内到逆元 然后用map 映射以分治点为起点的链的值a 成他的下标 u 然后暴力跑出以分治点儿子为起点的链的值b,然后在map里查找inv[b ...
- codeforces 161D Distance in Tree 树上点分治
链接:https://codeforces.com/contest/161/problem/D 题意:给一个树,求距离恰好为$k$的点对是多少 题解:对于一个树,距离为$k$的点对要么经过根节点,要么 ...
- POJ 1741 Tree 树上点分治
题目链接:http://poj.org/problem?id=1741 题意: 给定一棵包含$n$个点的带边权树,求距离小于等于K的点对数量 题解: 显然,枚举所有点的子树可以获得答案,但是朴素发$O ...
- 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 点分治
http://acm.hdu.edu.cn/showproblem.php?pid=4812 题意:有一棵树,每个点有一个权值要求找最小的一对点,路径上的乘积mod1e6+3为k 题解:点分治,挨个把 ...
- HDU 4812 D Tree 树分治
题意: 给出一棵树,每个节点上有个权值.要找到一对字典序最小的点对\((u, v)(u < v)\),使得路径\(u \to v\)上所有节点权值的乘积模\(10^6 + 3\)的值为\(k\) ...
- HDU 4812 D Tree 树分区+逆+hash新位置
意甲冠军: 特定n点树 K 以下n号码是正确的点 以下n-1行给出了树的侧. 问: 所以,如果有在正确的道路点图的路径 % mod = K 如果输出路径的两端存在. 多条路径则输出字典序最小的一条. ...
- [poj1741 Tree]树上点分治
题意:给一个N个节点的带权树,求长度小于等于K的路径条数 思路:选取一个点作为根root,假设f(root)是当前树的答案,那么答案来源于两部分: (1)路径不经过root,那么就是完全在子树内,这部 ...
随机推荐
- delphi中WebBrowser的parent改变时变成空白问题的解决(覆盖CreateWnd和DestroyWnd)
这段时间在做一个delphi界面打开网页的功能,且此网页所在窗口可完整显示,可缩小到另一个窗口的panel上显示 可是在改变网页所在窗口时,WebBrowser控件变成了空白 上网google了半天, ...
- 如何快速生成数据文件(fsutil命令,使用CreateFile和SetEndOfFile API函数,fopen和fseek RTL函数)
1. fsutil 命令 文件会瞬间生成,因为实际上Windows只是分配了名称.地址和空间给该文件,并没有读写任何文件内容 100M=1024x1024x100 fsutil file create ...
- System.InvalidOperationException: 未在本地计算机上注册“Microsoft.ACE.OLEDB.12.0”提供程序。
今天在写导入Excel表格的代码,运行时却报异常,异常信息: System.InvalidOperationException: 未在本地计算机上注册“Microsoft.ACE.OLEDB.12.0 ...
- 内存可用性判断 IsBadCodePtr IsBadReadPtr 等等
程序异常崩溃,多数是有内存访问异常引起.为定位崩溃位置通常考虑加强内存访问控制,如此有必要进行内存可用性判断,从<Windows核心编程>中看到内存指针的可用性判断方法,感觉还不错,此处记 ...
- Ext5.1日期控件仅显示年月
1.注册xtype类型 2.保存文件为xxxx.js 3.使用 xtype : monthfield return this.buildToolbar({ items: [ { xtype: 'mon ...
- c#实现类似数据的行锁
当我们有一些这样的需求,比如某个订单中下单,修改等等这些是单例执行的,不能同步操作,当然这样的情况你可以使用数据库的行锁来实现,但是我们代码里面实现的话 ,我们也要用到锁,大部分情况下我们使用lock ...
- MJPhotoBrowser 用法
一.使用方法: #import "MJPhotoBrowser.h" #import "MJPhoto.h" - (void)tapPhoto:(UIT ...
- XP下安装ubuntu
一,环境说明 dell vostro 1400笔记本,winxp sp3操作系统,ubuntu-9.10-desktop-i386.iso 写这篇随笔的时候我用的已经是ubuntu了. 我是在我的移动 ...
- 关于JDK和JRE的一些总结
一.关于JDK和JRE JDK (Java Development Kit)即java开发工具,包括JER及代码编译器(javac).文档注释器(JavaDoc).代码调试器(Java Debugge ...
- 转一个git的命令
Git远程操作详解 Git有很多优势,其中之一就是远程操作非常简便.本文详细介绍5个Git命令,它们的概念和用法,理解了这些内容,你就会完全掌握Git远程操作. git clone git rem ...