hdoj4812 D Tree(点分治)

题目链接:https://vjudge.net/problem/HDU-4812

题意:给定一颗带点权的树,求是否存在一条路经的上点的权值积取模后等于k,如果存在多组点对,输出字典序最小的。

思路:

  点分治模板题。按照套路,找重心,求出子树中节点到重心的权值积取模后的值dis[i](包括重心的权值,也不可以不包括,一样的),用id[j]记录该路径的端点,用的是点分治的第二种写法。递归时,用桶mine[i]记录到重心权值积为i的最小编号。然后查找时,对dis[j],满足要求的tmp为:tmp*dis[j]%MOD=k*V[u],因为我们的dis中包括重心的权值,所以重心的权值被乘了2次。然后用到了逆元,tmp=k*V[u]%MOD*inv[dis[j]]%MOD。更新完答案后就更新桶。

  因为没看清题目,题目求得点对(a,b)是a<b,而我以为(a,a)也满足,然后就找了两小时bug,崩溃到想锤电脑QAQ...。

AC代码:

#include<cstdio>
#include<algorithm>
#include<cctype>
#include<cstring>
using namespace std;

inline int read(){
    int x=0,f=0;char ch=0;
    while(!isdigit(ch)) {f|=ch=='-';ch=getchar();}
    while(isdigit(ch)) x=(x<<3)+(x<<1)+(ch^48),ch=getchar();
    return f?-x:x;
}

typedef long long LL;
const int maxn=1e5+5;
const int MOD=1e6+3;
const int maxk=1e6+5;
const int inf=0x3f3f3f3f;
struct node1{
    int v,nex;
}edge[maxn<<1];

struct node2{
    int x,y;
    node2(){x=y=0;}
    node2(int x,int y):x(x),y(y){}
}ans;

bool operator < (node2 a,node2 b){
    if(a.x==b.x) return a.y<b.y;
    return a.x<b.x;
}

int n,k,cnt,head[maxn],sz[maxn],mson[maxn],Min,root,size;
int vis[maxn],mine[maxk],id[maxn],t,tt;
LL V[maxn],dis[maxn],inv[maxk];

void init(){
    inv[1]=1;
    for(int i=2;i<maxk;++i)
        inv[i]=(MOD-MOD/i)*inv[MOD%i]%MOD;
}

void adde(int u,int v){
    edge[++cnt].v=v;
    edge[cnt].nex=head[u];
    head[u]=cnt;
}

void getroot(int u,int fa){
    sz[u]=1,mson[u]=0;
    for(int i=head[u];i;i=edge[i].nex){
        int v=edge[i].v;
        if(vis[v]||v==fa) continue;
        getroot(v,u);
        sz[u]+=sz[v];
        mson[u]=max(mson[u],sz[v]);
    }
    mson[u]=max(mson[u],size-sz[u]);
    if(mson[u]<Min) Min=mson[u],root=u;
}

void getdis(int u,int fa,LL len){
    dis[++t]=len,id[t]=u;
    for(int i=head[u];i;i=edge[i].nex){
        int v=edge[i].v;
        if(vis[v]||v==fa) continue;
        getdis(v,u,len*V[v]%MOD);
    }
}

void solve(int u){
    mine[V[u]]=u,t=0;
    for(int i=head[u];i;i=edge[i].nex){
        int v=edge[i].v;
        if(vis[v]) continue;
        tt=t;
        getdis(v,u,V[u]*V[v]%MOD);
        for(int j=tt+1;j<=t;++j){    
            LL tmp=1LL*k*V[u]%MOD*inv[dis[j]]%MOD;
            if(mine[tmp]==inf) continue;
            node2 other;
            if(mine[tmp]<id[j]) other.x=mine[tmp],other.y=id[j];
            else other.x=id[j],other.y=mine[tmp];
            if(other<ans) ans=other;
        }
        for(int j=tt+1;j<=t;++j)
            mine[dis[j]]=min(mine[dis[j]],id[j]);
    }
    mine[V[u]]=inf;
    for(int i=1;i<=t;++i)
        mine[dis[i]]=inf;
}

void fenzhi(int u,int ssize){
    vis[u]=1;
    solve(u);
    for(int i=head[u];i;i=edge[i].nex){
        int v=edge[i].v;
        if(vis[v]) continue;
        Min=inf,root=0;
        size=sz[v]<sz[u]?sz[v]:(ssize-sz[u]);
        getroot(v,0);
        fenzhi(root,size);
    }
}

int main(){
    init();
    memset(mine,0x3f,sizeof(mine));
    while(~scanf("%d%d",&n,&k)){
        cnt=0;
        ans.x=ans.y=n+1;
        for(int i=0;i<=n;++i)
            head[i]=vis[i]=0;
        for(int i=1;i<=n;++i){
            int tmp=read();
            V[i]=tmp;
        }
        for(int i=1;i<n;++i){
            int u=read(),v=read();
            adde(u,v);
            adde(v,u);
        }
        Min=inf,root=0,size=n;
        getroot(1,0);
        fenzhi(root,n);
        if(ans.x==n+1)
            printf("No solution\n");
        else
            printf("%d %d\n",ans.x,ans.y);
    }
    return 0;
}

 

posted @ 2019-08-21 21:58  Frank__Chen  阅读(194)  评论(0编辑  收藏  举报