【HDU 4812】D Tree【树的分治】

题意:给出一棵树,让你寻找一条路径,使得路径上的点相乘mod10^6+3等于k,输出路径的两个端点,按照字典序最小输出。

思路:这类问题很容易想到树的分治,每次找出树的重心,以重心为根,将树分成若干棵子树,然后对于每棵子树再一样的操作,现在就需要求一重心为根,寻找路径,依次遍历每一个子树,然后记录子树中点到根的权值的乘积X,然后通过在哈希表中寻找K×逆元(x),看是否存在,存在则更新答案。

#pragma comment(linker,"/STACK:102400000,102400000")
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
#define mod 1000003
#define N 100005
typedef long long ll;

struct E{
    int v, d, ne;
    E() {}
    E(int _v, int _ne):v(_v), ne(_ne){}
}e[N*2];

bool vis[N];
int size, head[N], ans[2], root, flag[mod], F[mod], sum[N], mi, cr, id[N];
ll val[N], ni[mod], path[N];

void init() {
    size = 0;
    memset(vis, false, sizeof(vis));
    memset(ans, -1, sizeof(ans));
    memset(head, -1, sizeof(head));
    memset(flag, 0, sizeof(flag));
}

void add(int u, int v) {
    e[size] = E(v, head[u]);
    head[u] = size++;
}

void dfs(int u, ll k) {
    int i, v;
    sum[u] = 1;
    vis[u] = true, id[cr] = u;
    path[cr++] = k*val[u]%mod;
    ll tm = path[cr-1];
    for (i = head[u];~i;i = e[i].ne) {
        v = e[i].v;
        if (vis[v]) continue;
        dfs(v, tm);
        sum[u] += sum[v];
    }
    vis[u] = false;
}

ll k;
int n, ca;
void getans(int a, int b) {
    if (a > b) swap(a,b);
    if (ans[0] == -1 || ans[0] > a) ans[0] = a, ans[1] = b;
    else if (ans[0] == a && ans[1] > b) ans[1] = b;
}

void getroot(int u) {
    int i, v, mx = 0;
    sum[u] = 1;
    vis[u] = true;
    for (i = head[u];~i;i = e[i].ne) {
        v = e[i].v;
        if (vis[v]) continue;
        getroot(v);
        sum[u] += sum[v];
        mx = max(mx, sum[v]);
    }
    mx = max(mx, sum[0]-sum[u]);
    if (mx < mi) mi = mx, root = u;
    vis[u] = false;
}
void cal(int u, int cnt) {
    if (cnt == 1) return;
    int i, v, j;
    mi = n;
    sum[0] = cnt;
    getroot(u);
    vis[root] = true;
    for (i = head[root];~i;i = e[i].ne) {
        v = e[i].v;
        if (vis[v]) continue;
        cr = 0;
        dfs(v, 1);
        for (j = 0;j < cr;j++) {
            if (path[j]*val[root]%mod == k) getans(root, id[j]);
            ll tm = k*ni[path[j]*val[root]%mod]%mod;
            if (flag[tm] != ca) continue;
            getans(F[tm], id[j]);
        }
        for (j = 0;j < cr;j++) {
            int tm = path[j];
            if (flag[tm] != ca || F[tm] > id[j]) F[tm] = id[j], flag[tm] = ca;
        }
    }
    ca++;
    for (i = head[root];~i;i = e[i].ne) {
        if (vis[e[i].v]) continue;
        cal(e[i].v, sum[e[i].v]);
    }
}

ll egcd(ll a,ll b, ll &x, ll &y) {//得到的是a*x+b*y=gcd(a,b)
	ll temp,tempx;
	if (b == 0) {
		x = 1;y = 0;
		return a;
	}
	temp = egcd(b,a % b, x, y);
	tempx = x;
	x = y;
	y = tempx - a / b * y;
	return temp;
}

int main() {
    int u, v, i, j;
    ll y;
    for (i = 0;i < mod;i++) {
        egcd(i*1ll, mod*1ll, ni[i], y);
        ni[i] %= mod, ni[i] = (ni[i]+mod)%mod;
    }
    while (~scanf("%d%I64d", &n, &k)) {
        init();
        ca = 1;
        for (i = 1;i <= n;i++) scanf("%I64d", &val[i]);
        for (i = 1;i < n;i++) {
            scanf("%d%d", &u, &v);
            add(u, v), add(v, u);
        }
        cal(1, n);
        if (ans[0] == -1) puts("No solution");
        else printf("%d %d\n", ans[0], ans[1]);
    }
}


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值