Hdu4812点分治

题意:在树上找一条链,使得链上点的乘积对1e6+3取模为k

由于mod是质数,a*b%mod = c -> b = inv[a] * k;剩下就是树上的点分治了,每次划分重心之后,将所有点到根的距离存入hash表,并且在存入hash表的同时,可以查找此子树内过根符合条件的路径端点,并更新答案,每次以重心处理一次后,清空hash表。

#pragma comment(linker,"/STACK:102400000,102400000")
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<iostream>
#include<string>
#include<queue>
#include<stack>
#include<list>
#include<stdlib.h>
#include<algorithm>
#include<vector>
#include<map>
#include<cstring>
#include<set>
using namespace std;
typedef long long LL;

const int mod = 1e6 + 3;
const int maxn = 1e5 + 20;
inline int read()
{
    int x = 0; char ch = getchar();
    while (ch<‘0‘ || ch>‘9‘)ch = getchar();
    while (ch >= ‘0‘&&ch <= ‘9‘) { x = x * 10 + ch - ‘0‘; ch = getchar(); }
    return x;
}

LL V[maxn];
struct Edge
{
    int to; int next;
}e[maxn * 2];

int len, mi, Root, num, n, k;
LL inv[mod + 100];
int head[maxn], Size[maxn], m[maxn], vis[maxn];
inline void add(int from, int to)
{
    e[len].to = to;
    e[len].next = head[from];
    head[from] = len++;
}

void initInv() //预处理逆元
{
    inv[0] = inv[1] = 1;
    for (int i = 2; i < mod + 10; i++) {
        inv[i] = ((-mod / i * inv[mod % i]) % mod + mod) % mod;
    }
}

void gaosize(int x, int fa)
{
    Size[x] = 1; m[x] = 0;
    for (int i = head[x]; i != -1; i = e[i].next) {
        int cc = e[i].to;
        if (cc == fa || vis[cc]) continue;
        gaosize(cc, x);
        Size[x] += Size[cc];
        m[x] = max(m[x], Size[cc]);
    }
}

void gaoroot(int root, int x, int fa)
{
    m[x] = max(m[x], Size[root] - Size[x]);
    if (mi > m[x]) {
        Root = x; mi = m[x];
    }
    for (int i = head[x]; i != -1; i = e[i].next) {
        int cc = e[i].to;
        if (vis[cc] || cc == fa) continue;
        gaoroot(root, cc, x);
    }
}

const int INF = 1e9 + 10;
int minl;
int minr;
void init()
{
    len = 0;
    minl = INF; minr = INF;
    memset(head, -1, sizeof(head));
    memset(vis, 0, sizeof(vis));
}

void up(int ll, int rr)
{
    if (rr < ll) swap(ll, rr);
    if (ll == minl) {
        if (rr < minr) {
            minl = ll; minr = rr;
        }
    }
    if (ll < minl) {
        minl = ll; minr = rr;
    }
}

int cnt, top;
int S[maxn * 10], S1[maxn * 10], pos[maxn * 10];
int color[mod * 2 + 1];
void gaodis(int x, int val, int fa, int root)  // 处理结点到Root的距离
{
    int  t = val%mod;
    if (t == k) up(x, root);
    t = t*inv[V[root]] % mod;
    int t1 = inv[t] * k%mod;
    //if (t==k) up(x, root); t值变了,我还在这用,真是蠢得不行
    S[top] = val;
    S1[cnt++] = val;//记录方案,最后清空color
    pos[top++] = x;
    if (color[t1]) {
        up(x, color[t1]);
    }
    for (int i = head[x]; i != -1; i = e[i].next) {
        int cc = e[i].to;
        if (cc == fa || vis[cc]) continue;
        gaodis(cc, val*V[cc] % mod, x, root);
    }
}
void gao(int x)
{
    cnt = 0;
    mi = n;
    gaosize(x, 0);
    gaoroot(x, x, 0); //找重心
    vis[Root] = 1;
    for (int i = head[Root]; i != -1; i = e[i].next) {
        int cc = e[i].to;
        top = 0;
        if (vis[cc])continue;
        gaodis(cc, V[Root] % mod*V[cc] % mod, Root, Root);
        for (int j = 0; j < top; j++) {
            int t = S[j];
            if (color[t] == 0) color[t] = pos[j];
            else color[t] = min(color[t], pos[j]);
        }
    }
    for (int i = 0; i < cnt; i++) color[S1[i]] = 0; // 清空hash表
    for (int i = head[Root]; i != -1; i = e[i].next) {
        int cc = e[i].to;
        if (vis[cc]) continue;
        gao(cc);
    }
}

int main()
{
    //freopen("1.in", "r", stdin);
    int a, b;
    initInv();
    memset(color, 0, sizeof(color));
    while (scanf("%d%d", &n, &k) != EOF) {
        k %= mod;
        init();
        for (int i = 1; i <= n; i++) V[i] = read();
        for (int i = 0; i < n - 1; i++) {
            a = read();
            b = read(); add(b, a); add(a, b);
        }
        gao(1);
        if (minl == INF)printf("No solution\n");
        else printf("%d %d\n", minl, minr);
    }
    return 0;
}
时间: 2024-10-12 20:09:56

Hdu4812点分治的相关文章

HDU4812 D Tree(树的点分治)

题目大概说给一棵有点权的树,输出字典序最小的点对,使这两点间路径上点权的乘积模1000003的结果为k. 树的点分治搞了.因为是点权过根的两条路径的LCA会被重复统计,而注意到1000003是质数,所以这个用乘法逆元搞一下就OK了.还有要注意“治”的各个实现,把时间复杂度“控制”在O(nlogn). WA了几次,WA在漏了点到子树根的路径,还有每次分治忘了清空数组. 1 #include<cstdio> 2 #include<cstring> 3 #include<algor

hdu4812 D Tree 点分治

这里要求输出字典序最小的两个点,就不能像之前那样容斥了,只能直接搞了. 直接搞的话,需要避开n^2,由于这里是等式,显然应该考虑hash映射.从前往后依次枚举计算每棵子树,对于每个子树结点,快速从前面已经计算过的子树中找到答案更新就可以了. 很简单的东西,只是难以用文字解释得清楚.大概一般点分治不用容斥直接搞大多是这样干吧. #pragma comment(linker,"/STACK:102400000,102400000") #include<iostream> #in

【点分治】【乘法逆元】hdu4812 D Tree

思路比较裸,但是要把答案存到哈希表里面,这里需要一定技巧,否则会被K=1且点权全是1的数据卡飞.预处理乘法逆元.TLE了一天.换了种点分治的姿势…… #pragma comment(linker,"/STACK:102400000,102400000") #include<cstdio> #include<algorithm> #include<cstring> using namespace std; int Res;char C; inline

LightOJ1257 Farthest Nodes in a Tree (II)(树的点分治)

题目给一棵树,边带有权值,求每一点到其他点路径上的最大权和. 树上任意两点的路径都可以看成是经过某棵子树根的路径,于是果断树分治. 对于每次分治的子树,计算其所有结点到根的距离:对于每个结点,找到另一个离根最远的且与该结点路径过根的结点,二者的距离和就是这个点在过这棵子树的根能到的最远距离. 现在问题就是怎么比较快地找到这另一个最远距离的点..两点路径过根,说明两点间不存在一点是另一点的祖先..我一开始还想用DFS序+线段树来着..想了想,想出了线性的算法: 记录每个结点属于根的哪个儿子,把当前

点分治练习: boatherds

[题面] 求一颗树上距离为K的点对是否存在 输入数据 n,m 接下来n-1条边a,b,c描述a到b有一条长度为c的路径 接下来m行每行询问一个K 输出数据 对于每个K每行输出一个答案,存在输出“AYE”,否则输出”NAY”(不包含引号) 数据范围 对于30%的数据n<=100 对于60%的数据n<=1000,m<=50 对于100%的数据n<=10000,m<=100,c<=1000,K<=10000000 [思路] 树分治. 离线存储m个询问.分治判断该m个询问

hdu4812 D Tree

树上的点分治. 首先为了避免超时预处理逆元,对于x(<mod),我们希望找到f-1(x) = y 满足y * x % mod = k. 由存在a, b : x *a + y * b = gcd(x, y), mod 为素数,故有 x * a + mod * b = 1. 由EXTENDED-EUCLID找出a (< mod). 对于树上的分治,首先找出当前树的重心,处理剔除重心后的各个子树,合法链属于其中一个子树或者经过树根. acm.hdu.edu.cn/showproblem.php?pi

HDU 5269 &amp;&amp; BestCoder #44 1002 ZYB loves Xor I (分治)

题目地址:HDU 5269 比赛的时候想到了分治的思路,但是脑残了.,.写麻烦了...调了好久也没调出来..(分治写的太少..)赛后优化了一下..就过了.. 我的思路是先排序,排序是按照的将每个数字的二进制表示倒过来的字典序从大到小排,比如样例2中的2,6,5,4,0,二进制分别是010,110,101,100,000,排序之后是 101 110 010 100 000 这样的话就把后缀相同的都给放在一块了.其实也相当于字典树,不过比赛的时候没想到字典树,只想到了分治.. 然后从末位开始找,对所

算法基础:分治模式,归并排序ΘΘΘΘΘΘ知识小结

1.分治模式在每层递归时都有三个步骤:分解,解决,合并 2.归并排序算法完全遵循分治模式: 分解:分解待排序的n个元素的序列成各具n/2个元素的两个子序列 解决:使用归并排序递归的排序两个子序列 合并:合并两个已排序的子序列以产生已排序的答案 3.分析分治算法所需要的时间计算: 假设T(n)是规模为n的一个问题的运行时间,若问题足够小,如对某个常量c,n≦c,则直接求解需要常量时将,我们将其写作Θ(1).假设吧原问题分解成a个子问题,每个子问题的规模是原问题的1/b(对归并排序,a和b都为2,然

Codeforces 97B Superset 平面分治

题目链接:点击打开链接 题意: 给定一个点集 添加一些点后再把这个点集输出来. 添加完点后使得对于点集内任意2个点都满足下面2条中至少一条 1.在同一水平线上或在同一垂直线上 2.所围成的矩阵里有其他点. 思路: 平面分治 先把点按x轴排序,然后找到中间的点,做一条直线 x = a[mid].x; 然后把所有点都投影到这条直线上,那么对于左边的点就不需要再和右边的进行匹配了. #pragma comment(linker, "/STACK:1024000000,1024000000")