洛谷 P2195 HXY造公园

题目描述

现在有一个现成的公园,有n个休息点和m条双向边连接两个休息点。众所周知,HXY是一个SXBK的强迫症患者,所以她打算施展魔法来改造公园并即时了解改造情况。她可以进行以下两种操作:

1、对某个休息点x,查询公园中所有经过该休息点的路径中的最长路径。

2、对于两个休息点x、y,如果x,y已经可以互相到达则忽略此次操作。否则,在x可到达的所有休息点和y可到达的所有休息点(包括x,y自身)分别选择一个休息点,然后在这两个休息点之间连一条边,并且这个选择应该满足对于连接后的公园,x和y所在的区域(即x,y可达到的所有休息点和边组成的集合)中的最长路径的长度最小。

HXY打算进行q个操作,请你回答她的对于公园情况的询问(操作1)或者执行她的操作(操作2)。

注:所有边的长度皆为1。保证不存在环。最长路径定义为:对于点v1,v2......vk,如果对于其中任意的vi和vi+1(1<=i<=k-1),都有边相连接,那么vj(1<=j<=k)所在区域的最长路径就是k-1。

输入输出格式

输入格式:

 

第一行,三个正整数,分别为n,m,q。

接下来的m行,每一行有两个正整数xi,yi,表示xi和yi有一条双向边相连。

再接下来的q行,每一行表示一个操作。

若该行第一个数为1,则表示操作1,之后还有一个正整数xi,表示要查询的休息点。

若该行第一个数为2,则表示操作2,之后还有两个正整数xi,yi,表示需要执行操作的两个休息点。

 

输出格式:

 

输出行数为操作1的个数。

每行输出对于操作1询问的回答。

 

输入输出样例

输入样例#1: 复制
6 0 6
2 1 2
2 3 4
2 5 6
2 3 2
2 5 3
1 1
输出样例#1: 复制
4

说明

数据范围:

对于10%的数据,只存在操作1。

对于30%的数据,1<=m<n<=20,1<=q<=5。

对于60%的数据,1<=m<n<=2000,1<=q<=1000。

对于100%的数据,1<=m<n<=3*10^5,1<=q<=3*10^5。

思路:总的来说,用并查集做辅助工具,然后预处理出最长链,题目并不难。但是还存在一个问题:

  那就是在一棵树中,经过这个点的所有的路径中最长的为什么就是这棵树的直径。题解是这么写的,但是划拉了一下,不是很明白为什么。

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define MAXN 2010
using namespace std;
int n,m,q,tot;
int fa[MAXN],dis[MAXN];
int sum1[MAXN],sum2[MAXN],dad[MAXN];
int mid[MAXN],lcnt[MAXN],rcnt[MAXN];
int to[MAXN*2],net[MAXN*2],head[MAXN];
int find(int x){ return fa[x]==x?x:fa[x]=find(fa[x]); }
void add(int u,int v){
    to[++tot]=v;net[tot]=head[u];head[u]=tot;
    to[++tot]=u;net[tot]=head[v];head[v]=tot;
}
void dfs(int now){
    for(int i=head[now];i;i=net[i])
        if(dis[to[i]]==-1){
            dis[to[i]]=dis[now]+1;
            dfs(to[i]);
        }
}
void dfs1(int now){
    for(int i=head[now];i;i=net[i])
        if(dad[now]!=to[i]){
            dad[to[i]]=now;
            sum1[to[i]]=sum1[now]+1;
            dfs1(to[i]);
        }
}
void dfs2(int now){
    for(int i=head[now];i;i=net[i])
        if(dad[now]!=to[i]){
            dad[to[i]]=now;
            sum2[to[i]]=sum2[now]+1;
            dfs2(to[i]);
        }
}
int main(){
    scanf("%d%d%d",&n,&m,&q);
    for(int i=1;i<=n;i++)    fa[i]=i;
    for(int i=1;i<=m;i++){
        int x,y;scanf("%d%d",&x,&y);
         add(x,y);int dx=find(x),dy=find(y);
         if(dx==dy)    continue;
         fa[dy]=dx;
    }
    for(int i=1;i<=n;i++)
        if(find(i)==i){
            int src;src=i;
            memset(dis,-1,sizeof(dis));
            dis[src]=0;dfs(src);
            for(int j=1;j<=n;j++)
                if(dis[j]>dis[src])    src=j;
            memset(dis,-1,sizeof(dis));
            dis[src]=0;dfs(src);lcnt[i]=src;
            for(int j=1;j<=n;j++)
                if(dis[j]>dis[src])    src=j;
            rcnt[i]=src;int minn=0x7f7f7f7f;
            dfs1(src);memset(dad,0,sizeof(dad));
            dfs2(src);memset(dad,0,sizeof(dad));
            for(int j=1;j<=n;j++)
                if(find(j)==i){
                    int len=abs(sum1[j]-sum2[j]);
                    if(len<minn){ minn=len;mid[i]=j; }
                }
            memset(sum1,0,sizeof(sum1));
            memset(sum2,0,sizeof(sum2));
        }
    for(int i=1;i<=q;i++){
        int opt,x,y;
         scanf("%d%d",&opt,&x);
        if(opt==1){
            memset(dis,-1,sizeof(dis));
            dis[x]=0;dfs(x);int dx=find(x);
            int ans=max(dis[lcnt[dx]],dis[rcnt[dx]]);
            int maxn=0,ops;
            if(dis[lcnt[dx]]>dis[rcnt[dx]])    ops=lcnt[dx];
            else ops=rcnt[dx];
            dfs1(lcnt[dx]);memset(dad,0,sizeof(dad));
            dfs2(rcnt[dx]);memset(dad,0,sizeof(dad));
            for(int j=1;j<=n;j++)
                if(dx==find(j)&&i!=j&&j!=ops&&(sum1[j]-sum2[j]==sum1[x]-sum2[x]||j==lcnt[dx]||j==rcnt[dx]))
                    maxn=max(maxn,dis[j]);
            cout<<ans+maxn<<endl;
            memset(sum1,0,sizeof(sum1));
            memset(sum2,0,sizeof(sum2));
         }
        else if(opt==2){
             scanf("%d",&y);
             int dx=find(x),dy=find(y);
             if(dx==dy)    continue;fa[dy]=dx;
             add(mid[dx],mid[dy]);
             memset(dis,-1,sizeof(dis));
             int src;src=x;
            dis[src]=0;dfs(src);
            for(int j=1;j<=n;j++)
                if(dis[j]>dis[src])    src=j;
            memset(dis,-1,sizeof(dis));
            dis[src]=0;dfs(src);lcnt[dx]=src;
            for(int j=1;j<=n;j++)
                if(dis[j]>dis[src])    src=j;
            rcnt[dx]=src;int minn=0x7f7f7f7f;
            dfs1(lcnt[dx]);memset(dad,0,sizeof(dad));
            dfs2(rcnt[dx]);memset(dad,0,sizeof(dad));
            for(int j=1;j<=n;j++)
                if(find(j)==dx){
                    int len=abs(sum1[j]-sum2[j]);
                    if(len<minn){ minn=len;mid[dx]=j; }
                }
            memset(sum1,0,sizeof(sum1));
            memset(sum2,0,sizeof(sum2));
        }
    }
}
/*
6 0 6
2 1 2
2 3 4
2 5 6
2 3 2
2 5 3
1 1
*/
不知道为什么wa了3个点的30分暴力

70分的dfs:

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define MAXN 300010
using namespace std;
int n,m,q,tot;
int fa[MAXN],dis[MAXN],len[MAXN];
int sum1[MAXN],sum2[MAXN],dad[MAXN];
int mid[MAXN],lcnt[MAXN],rcnt[MAXN];
int to[MAXN*2],net[MAXN*2],head[MAXN];
int find(int x){ return fa[x]==x?x:fa[x]=find(fa[x]); }
void add(int u,int v){
    to[++tot]=v;net[tot]=head[u];head[u]=tot;
    to[++tot]=u;net[tot]=head[v];head[v]=tot;
}
void dfs(int now){
    for(int i=head[now];i;i=net[i])
        if(dis[to[i]]==-1){
            dis[to[i]]=dis[now]+1;
            dfs(to[i]);
        }
}
int main(){
    scanf("%d%d%d",&n,&m,&q);
    for(int i=1;i<=n;i++)    fa[i]=i;
    for(int i=1;i<=m;i++){
        int x,y;scanf("%d%d",&x,&y);
         add(x,y);int dx=find(x),dy=find(y);
         if(dx==dy)    continue;    fa[dy]=dx;
    }
    
    for(int i=1;i<=n;i++)
        if(find(i)==i){
            int src;src=i;
            
            memset(dis,-1,sizeof(dis));
            dis[src]=0;dfs(src);
            for(int j=1;j<=n;j++)
                if(dis[j]>dis[src])    src=j;
            lcnt[i]=src;
                
            memset(dis,-1,sizeof(dis));
            dis[src]=0;dfs(src);
            for(int j=1;j<=n;j++)
                if(dis[j]>dis[src])    src=j;
            rcnt[i]=src;len[i]=dis[src];
        }
    for(int i=1;i<=q;i++){
        int opt,x,y;
         scanf("%d%d",&opt,&x);
        if(opt==1)    cout<<len[find(x)]<<endl;
        else if(opt==2){
             scanf("%d",&y);
             int dx=find(x),dy=find(y);
             if(dx==dy)    continue;fa[dy]=dx;
            int a=len[dx],b=len[dy];
            len[dx]=(len[dx]+1)/2+(len[dy]+1)/2+1;
            if(len[dx]<a)    len[dx]=a;
            if(len[dx]<b)    len[dx]=b;
        }
    }
}
/*
6 0 6
2 1 2
2 3 4
2 5 6
2 3 2
2 5 3
1 1
*/

因为是RE和TLE,所以说,我怀疑是因为树太长了,所以爆栈了。

改成bfs就能AC了,但是我改了改,没改出来,所以就不改了。但是思路是正确的。

#include<queue>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define MAXN 300010
using namespace std;
int n,m,q,tot;
struct nond{ int pos,dis; };
int fa[MAXN],dis[MAXN],len[MAXN];
int sum1[MAXN],sum2[MAXN],dad[MAXN];
int mid[MAXN],lcnt[MAXN],rcnt[MAXN];
int to[MAXN*2],net[MAXN*2],head[MAXN];
int find(int x){ return fa[x]==x?x:fa[x]=find(fa[x]); }
void add(int u,int v){
    to[++tot]=v;net[tot]=head[u];head[u]=tot;
    to[++tot]=u;net[tot]=head[v];head[v]=tot;
}
int bfs(int x){
    queue<nond>que;
    nond tmp;tmp.pos=x;tmp.dis=0;
    que.push(tmp);int maxn=0,opt=x;
    while(!que.empty()){
        nond now=que.front();que.pop();
        for(int i=head[now.pos];i;i=net[i]){
            nond tmx;tmx.pos=to[i];tmx.dis=now.dis+1;
            que.push(tmx);
            if(tmx.dis>maxn){ maxn=tmx.dis;opt=to[i]; }
        }
    }
    while(!que.empty())    que.pop();
    tmp.pos=opt;tmp.dis=0;
    que.push(tmp);
    while(!que.empty()){
        nond now=que.front();que.pop();
        for(int i=head[now.pos];i;i=net[i]){
            nond tmx;tmx.pos=to[i];tmx.dis=now.dis+1;
            que.push(tmx);
            if(tmx.dis>maxn){ maxn=tmx.dis; }
        }
    }
    return maxn;
}
int main(){
    scanf("%d%d%d",&n,&m,&q);
    for(int i=1;i<=n;i++)    fa[i]=i;
    for(int i=1;i<=m;i++){
        int x,y;scanf("%d%d",&x,&y);
         add(x,y);int dx=find(x),dy=find(y);
         if(dx==dy)    continue;    fa[dy]=dx;
    }
    for(int i=1;i<=n;i++)
        if(find(i)==i)    len[i]=bfs(i);
    for(int i=1;i<=q;i++){
        int opt,x,y;
         scanf("%d%d",&opt,&x);
        if(opt==1)    cout<<len[find(x)]<<endl;
        else if(opt==2){
             scanf("%d",&y);
             int dx=find(x),dy=find(y);
             if(dx==dy)    continue;fa[dy]=dx;
            int a=len[dx],b=len[dy];
            len[dx]=(len[dx]+1)/2+(len[dy]+1)/2+1;
            if(len[dx]<a)    len[dx]=a;
            if(len[dx]<b)    len[dx]=b;
        }
    }
}
/*
6 0 6
2 1 2
2 3 4
2 5 6
2 3 2
2 5 3
1 1
*/
改错的10分的代码
细雨斜风作晓寒。淡烟疏柳媚晴滩。入淮清洛渐漫漫。 雪沫乳花浮午盏,蓼茸蒿笋试春盘。人间有味是清欢。
原文地址:https://www.cnblogs.com/cangT-Tlan/p/8849023.html