hdu4812 逆元+树分治

逆元链接:https://www.cnblogs.com/zzqc/p/7192436.html

经典的树分治题

#pragma comment("linker,"/STACK:102400000,102400000)
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define MAXN 100005
#define mod 1000003
typedef long long ll
struct E{
    int v,d,next;
    E(){}
    E(int vv,int nn):v(vv),next(nn){}
}e[MAXN<<1];
bool vis[MAXN];
int size,head[MAXN],ans[2];//边和答案
int flag[mod],F[mod],id[MAXN];//哈希
ll ni[mod];//1-mod之间的逆元表
int sum[MAXN],mi,cr;//cr是子树的路径数量
ll val[MAXN],path[MAXN];//点权路径
void init(){
    size=0;
    memset(vis,0,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 tmp=path[cr-1];
    for(i=head[u];i!=-1;i=e[i].next){
        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!=-1;i=e[i].next){
        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;//树规模为cnt
    getroot(u);
    vis[root]=true;
    for(i=head[root];i~=-1;i=e[i].next){
        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 tmp=k*ni[path[j]*val[root]%mod]%mod;//k*x逆元=y
            if(flag[tmp]!=ca) continue;//没有在当前树中找到路径
            getans(F[tmp],id[j]);//F[tmp]是值为tmp的path对应的终点编号,id[j]是当前路径的终点编号
        }
        //把当前子树更新到目前哈希表中
        for(j=0;j<cr;j++){
            int tmp=path[j];
            if(flag[tmp]!=ca || F[tmp]>id[j])//如果当前哈希表状态中没有path[j],或者F[tmp]的结点编号没有优化到最小
                F[tm]=id[j],flag[tm]=ca;
        }
    }
    ca++;//每次搜完一颗子树,颜色数+1
    //这里就是分治
    for(i=head[root];i!=-1;i=e[i].next){
        if(vis[e[i].v])
            continue;
        cal(e[i].v,sum[e[i].v]);
    }
}
//拓展欧几里得打表,求的逆元就是最后的x
ll egcd(ll a,ll b,ll &x,ll &y){
    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%lld",&n,&k)==2){
        init();
        ca=1;//不能是0
        for(i=1;i<=n;i++) scanf("%lld",&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]);
    }
}

 

posted on 2018-10-29 21:51  zsben  阅读(116)  评论(0编辑  收藏  举报

导航