hdu 4812 (点分治+逆元)

hdu 4812 (点分治+逆元)

题意:

找出一条路径,点上乘积模 1 e 6 + 3 1e6+3 1e6+3的值=k,(u,v)字典序最小

思路:

树上路径判定问题,显然可以用点分的第二种写法解决。

a [ i ] ∗ a [ j ] ≡ k ( m o d ( 1 e 6 + 3 ) ) a[i]*a[j]\equiv k(mod (1e6+3)) a[i]a[j]k(mod(1e6+3))

对于这种点权相关点分治的第二种写法,在分治重心统计的时候可以先把根忽略,存储不包含根的路径(点权转边权,根点权消失),先统计点数>=2的路径,最后再单独考虑单点贡献,不容易出错

a [ c h a ] ≡ a [ x ] ∗ a [ n o w ] − 1 ∗ k ( m o d ( 1 e 6 + 3 ) ) a[cha]\equiv a[x]*a[now]^{-1}*k(mod(1e6+3)) a[cha]a[x]a[now]1k(mod(1e6+3))

查的时候把a[x]带上即可

注意这样只统计了点数为2的路径,但是这题实际上没有单点情况(坑了我贼久!!) 路径保证至少2个点了

还有注意点分治统计完的时候一定要回溯贡献!!

#pragma comment(linker,"/STACK:102400000,102400000")
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define fi first 
#define se second 
using  namespace std;
typedef pair<int,int>P;
const int maxn=1e5+5;
const int mod=1e6+3;
const int M=1e6+10;
int head[maxn],ver[maxn<<1],next1[maxn<<1],sz[maxn],mxson[maxn],S,rt,ans1,ans2,mp[M],inv[M],n,k,tot,a[maxn],cnttmp,d[maxn];
P tmp[maxn];
bool v[maxn];
void add(int x,int y){
    ver[++tot]=y,next1[tot]=head[x],head[x]=tot;
}
void init1(){
    inv[1]=1;
    for(int i=2;i<M;++i)inv[i]=1ll*(mod-mod/i)*inv[mod%i]%mod;  
}
void getRoot(int x,int f){
    sz[x]=1;mxson[x]=0;
    for(int i=head[x];i;i=next1[i]){
        int y=ver[i];
        if(y==f||v[y])continue;
        getRoot(y,x);
        sz[x]+=sz[y];
        mxson[x]=max(mxson[x],sz[y]);
    }
    mxson[x]=max(mxson[x],S-mxson[x]);
    if(mxson[x]<mxson[rt])rt=x;
}
void init(int x){
    S=sz[x];
    mxson[rt=0]=maxn;
    getRoot(x,0);
}
void getQue(int x,int f,int val){
    tmp[++cnttmp]={val,x};
    for(int i=head[x];i;i=next1[i]){
        int y=ver[i];
        if(v[y]||y==f)continue;
        getQue(y,x,1ll*val*a[y]%mod);
    }
}
void update(int x,int y){
    if(x==y)return;
    if(x>y)swap(x,y);
    if(x<ans1||(x==ans1&&y<ans2))ans1=x,ans2=y;
}
void cal(int x){
    mp[1]=x;
    int num=0;
    for(int i=head[x];i;i=next1[i]){
        int y=ver[i];
        if(v[y])continue;
        cnttmp=0;
        getQue(y,x,a[y]);
        for(int i=1;i<=cnttmp;++i){
            int flag=1ll*inv[tmp[i].fi]*inv[a[x]]%mod*k%mod;
            if(mp[flag])update(tmp[i].se,mp[flag]);
        }
        for(int i=1;i<=cnttmp;++i){
            if(!mp[tmp[i].fi]||mp[tmp[i].fi]>tmp[i].se)mp[tmp[i].fi]=tmp[i].se;
            d[++num]=tmp[i].fi;
        }
    }   
    for(int i=num;i;--i)mp[d[i]]=0;
    mp[1]=0;
}
void dfz(int x){
    v[x]=1;
    cal(x);
    for(int i=head[x];i;i=next1[i]){
        int y=ver[i];
        if(v[y])continue;
        init(y);
        dfz(rt);
    }
}
int main(){
    init1();
    int x,y;
    while(~scanf("%d%d",&n,&k)){
        tot=0;
        ans1=ans2=maxn;
        for(int i=1;i<=n;++i)head[i]=0,v[i]=0;
        for(int i=1;i<=n;++i)scanf("%d",&a[i]);
        for(int i=1;i<n;++i){
            scanf("%d%d",&x,&y);
            add(x,y);add(y,x);
        }
        S=n;
        mxson[rt=0]=maxn;
        getRoot(1,0);
        dfz(rt);
        if(ans1==maxn)puts("No solution");
        else cout<<ans1<<" "<<ans2<<"\n";   
    }
    return 0;
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值