洛谷 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询问的回答。
输入输出样例
说明
数据范围:
对于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。
思路:总的来说,用并查集做辅助工具,然后预处理出最长链,题目并不难。但是还存在一个问题:
那就是在一棵树中,经过这个点的所有的路径中最长的为什么就是这棵树的直径。题解是这么写的,但是划拉了一下,不是很明白为什么。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#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 */
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了,但是我改了改,没改出来,所以就不改了。但是思路是正确的。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#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 */