HDU 4916 Count on the path

HDU 4916 Count on the path

Part 0

不要理题目里说的让你用手写读入挂,会TLE,用G++还有scanf就行了。(超级读入挂没事)C++也可以AC,不用加栈外挂(如果实在栈溢出也可以加)

Part 1

一个性质:假如这两个点的路径不经过点1,那么答案就是1。

那么我们以1为根结点将这颗无根树变为有根树。若这条路径不经过点1,那么答案就是除路径外的最小值(这不废话)。

这时候我们可以删去1这个节点,于是整棵树就变成了森林。于是,我们以每颗树中原父亲为1的那颗树的根节点。然后我们预处理出每个点除了它到它所在的树的根节点路径上的点的它所在树内节点的编号最小值,记为 d p [ i ] dp[i] ,最后答案就是两个点 d p dp 值,还有其他树内结点编号的最小值。

预处理这些东西先处理出每个点所对应子树的编号最小值,然后再用树形DP就可以了。判两个结点的路径是否经过点1就是询问两个结点是否在同一颗树内(删去结点1之后的),这个可以将每颗树染色或是用并查集。

同时注意一下一个端点是1的情况,可以特判或是将dp[1]赋值为一个很大的数。

然后贴上代码:

#include<cstdio>
#include<algorithm>
#include<cstring>
#define M 1000005
using namespace std;
void check_min(int &x,int y){if(x>y)x=y;}
struct E{
    int to,nx;
}edge[M<<1];
int tot,head[M];
void Addedge(int a,int b){//正向表存边 
    edge[++tot].to=b;
    edge[tot].nx=head[a];
    head[a]=tot;
}
struct node{
    int d,Belong;
    bool operator <(const node &x)const{
        return d<x.d;
    }
    bool operator !=(const int &x)const{//判断是否从属于同一树内(Belong太长不想打) 
        return Belong!=x;
    }
    void clear(){d=1e9;}
    void Print(){printf("this-->%d %d\n",d,Belong);}
}A[4];
int fa[M],Belong[M];
int mn_id[M],dp[M];//mn_id[i] i这个子树内最小编号 ,dp数组含义在题解里讲过了 
void dfs(int now){
    mn_id[now]=now;
    for(int i=head[now];i;i=edge[i].nx){
        int nxt=edge[i].to;
        if(nxt==fa[now])continue;
        fa[nxt]=now;
        if(now!=1)Belong[nxt]=Belong[now];//染色 
        dfs(nxt);
        check_min(mn_id[now],mn_id[nxt]);
    }
}
void redfs(int now,int pre){
    dp[now]=pre;//此刻的dp数组还没有与当前节点的子树(除当前节点外)的点的信息更新 
    int mn1=1e9,mn2=1e9;
    for(int i=head[now];i;i=edge[i].nx){
        int nxt=edge[i].to;
        if(nxt==fa[now])continue;
        check_min(dp[now],mn_id[nxt]);//与当前节点的子树(除当前节点外)内的点的信息更新 
        if(mn_id[nxt]<mn1)mn2=mn1,mn1=mn_id[nxt];//存当前节点的子节点中最小编号最小的两个子树的值最小编号(有点绕口QWQ) 
        else if(mn_id[nxt]<mn2)mn2=mn_id[nxt];
    }
    for(int i=head[now];i;i=edge[i].nx){
        int nxt=edge[i].to,nxtd;
        if(nxt==fa[now])continue;
        if(mn1==mn_id[nxt])nxtd=mn2;//如果这个最小值就在这个nxt的子树中 
        else nxtd=mn1;
        redfs(nxt,min(pre,nxtd));
    }
}
int n,m;
void Init(){
    tot=0;dp[1]=1e9;//一个端点是1的情况也要注意 
    memset(head,0,sizeof(head));
}
int main(){
    while(~scanf("%d%d",&n,&m)){
		
        Init();
        for(int i=1;i<=n;i++)Belong[i]=i;//初始化Belong数组(也就是染色的数组) 
        for(int i=1;i<n;i++){
            int a,b;
            scanf("%d%d",&a,&b);//千万不要用读入挂 
            Addedge(a,b);
            Addedge(b,a);
        }
        dfs(1);
        for(int i=1;i<=3;i++)A[i].clear();//处理最小编号前三大的子树 
        for(int i=head[1];i;i=edge[i].nx){
            int nxt=edge[i].to;
            redfs(nxt,1e9);
            node P;
            P.d=mn_id[nxt],P.Belong=nxt;
            if(P<A[1])A[3]=A[2],A[2]=A[1],A[1]=P;
            else if(P<A[2])A[3]=A[2],A[2]=P;
            else if(P<A[3])A[3]=P;
        }
        int ans=0;
        while(m--){
            int a,b;
            scanf("%d%d",&a,&b);
            a^=ans,b^=ans;
            if(a==b&&a==1){//这个不判也能A 
            	puts("2");
            	continue;
			}
            if(Belong[a]==Belong[b]){//如果两个结点之间的路径不经过1 
                puts("1");
                ans=1;
                continue;
            }
            ans=min(dp[a],dp[b]);//取两个点的dp值 
            a=Belong[a],b=Belong[b];//**
            if(A[1]!=a&&A[1]!=b)check_min(ans,A[1].d);//判断是否同属同一集合 
            else if(A[2]!=a&&A[2]!=b)check_min(ans,A[2].d);
            else if(A[3]!=a&&A[3]!=b)check_min(ans,A[3].d);
            printf("%d\n",ans);
        }
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_35320178/article/details/87023562