HDU-4812 D Tree

Posted artoriax

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了HDU-4812 D Tree相关的知识,希望对你有一定的参考价值。

题面

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 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")

题意

找出路径乘积等于k的点对,如果有多个点对,输出字典序最小的

题解

这个题的树分治和之前大不一样,我想了好久

不一样的地方在于,这个题是让求一个合法的,字典序最小的点对,之前点分治是什么,先加上一个答案,再减去一个答案,得到最后的答案,而这个题,需要的找出,且字典序最小。

我们就按这个意思来模拟,我们的getdis函数基本保持不变,不过要记录一下路径长度,还有该路径对应的终止的点(起始点是再calc时确定的)我们在分治一颗树的时候,对于每一个儿子,对其getdis,此时,我们是不加入u结点的,这个时候,我们就可以统计,以u结点为起始结点的答案,还有这颗子树,与之前处理过的子树的答案,我们怎么合并呢?我们再记录一个mp,记录某一路径长度下,最小的结点标号,找(dis[i]*dis[j] \% mod=k),也就是找是否存在(dis[j]=k*inv[dis[i]] \% mod)其中inv表示逆元。我们用map记录dis[j]对应的编号即可,这样就可以统计答案了。统计完这颗子树的答案,我们还要把这颗子树合并到处理过的子树中去,也就是更新一下mp。

最后,由于我们这次分治的结果与其他分治的树是毫无联系的,我们需要清空mp数组,(这里提一下,这里的map不是指stl的map,开一个数组就够了,开map反而可能超时),具体方法是再遍历一遍子树,之前怎么加入map的,就怎么清空就可以了

然后就进入分治的循环,分治子树即可

注意逆元也要直接全部预处理出来,否则会超时,直接费马小定理预处理即可

复杂度(nlogn)

代码

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll mod = 1e6 + 3;
const int N = 1e5 + 50;
vector<int> G[N];
int sze[N];
int msze[N];
int vis[N];
int maxx;
int S;
int root;
void getroot(int u, int f) {
    sze[u] = 1;
    msze[u] = 0;
    for (int i = 0; i < G[u].size(); i++) {
        int v = G[u][i];
        if (vis[v] || v == f) continue;
        getroot(v, u);
        sze[u] += sze[v];
        msze[u] = max(msze[u], sze[v]);
    }
    msze[u] = max(msze[u], S - sze[u]);
    if (msze[u] < maxx) {
        maxx = msze[u];
        root = u;
    }
}
struct Ans {
    int x, y;
    Ans(int _x = 0, int _y = 0) {
        x = min(_x, _y);
        y = max(_x, _y);
    }
    bool operator < (const Ans &b) const {
        return x == b.x ? y < b.y : x < b.x;
    }
};
Ans ans = Ans(1e9, 1e9);
ll c[N];
const int M = 1e6 + 50;
ll inv[M];
ll dis[N];
int id[N];
int mp[M];
int cnt = 0;
ll k;
void getdis(int u, int f, ll w) {
    dis[++cnt] = w;
    id[cnt] = u;
    for (int i = 0; i < G[u].size(); i++) {
        int v = G[u][i];
        if (v == f || vis[v]) continue;
        getdis(v, u, w * c[v] % mod);
    }
}
ll qpow(ll a, ll b) {
    ll ans = 1;
    while (b) {
        if (b & 1) ans = ans * a % mod;
        a = a * a % mod;
        b >>= 1;
    }
    return ans;
}
void dfs(int u) {
    vis[u] = 1;
    mp[c[u]] = u;//计算经过u点的合法路径
    for (int i = 0; i < G[u].size(); i++) {
        int v = G[u][i];
        if (vis[v]) continue;
        cnt = 0; getdis(v, u, c[v]);
        for (int j = 1; j <= cnt; j++) {//计算一端为u点的以及子树之间的合法路径
            ll x = inv[dis[j]] * k % mod;
            int y = mp[x];
            if (y == 0) continue;
            Ans tmp = Ans(y, id[j]);
            if (tmp < ans) ans = tmp;
        }
        cnt = 0; getdis(v, u, c[u] * c[v] % mod);
        for (int j = 1; j <= cnt; j++) {//加入子树之间的合法路径
            int now = mp[dis[j]];
            if (now == 0 || id[j] < now) mp[dis[j]] = id[j];//同一路径长度统计最小的
        }
    }
    mp[c[u]] = 0;//因为子树与父亲无联系,所以清空全部mp
    for (int i = 0; i < G[u].size(); i++) {
        int v = G[u][i];
        if (vis[v]) continue;
        cnt = 0;
        getdis(v, u, c[u] * c[v] % mod);
        for (int j = 1; j <= cnt; j++) {
            mp[dis[j]] = 0;
        }
    }
    for (int i = 0; i < G[u].size(); i++) {
        int v = G[u][i];
        if (vis[v]) continue;
        root = 0;
        maxx = 1e9;
        S = sze[v];
        getroot(v, u);
        dfs(root);
    }
}
int main() {
    int n;
    for (int i = 0; i < mod; i++) {
        inv[i] = qpow(i, mod - 2);
    }
    while (~scanf("%d%lld", &n, &k)) {
        for (int i = 1; i <= n; i++) {
            scanf("%lld", &c[i]);
            G[i].clear();
        }
        for (int i = 1; i < n; i++) {
            int u, v;
            scanf("%d%d", &u, &v);
            G[u].push_back(v);
            G[v].push_back(u);
        }
        ans = Ans(1e9, 1e9);
        memset(vis, 0, sizeof(vis));
        maxx = 1e9;
        S = n;
        root = 0;
        getroot(1, 0);
        dfs(root);
        if (ans.x == 1e9) puts("No solution");
        else printf("%d %d
", ans.x, ans.y);
    }

    return 0;
}

以上是关于HDU-4812 D Tree的主要内容,如果未能解决你的问题,请参考以下文章

hdu4812 D Tree 点分治

HDU4812 D Tree(树的点分治)

hdu 4812 DTree (点分治)

四维偏序(K-D-Tree+rebuild)

添加片段时的 FlyOut 菜单设计问题

java 反射代码片段