题目描述

现在有一个现成的公园,有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*],net[MAXN*],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]]==-){
dis[to[i]]=dis[now]+;
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]+;
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]+;
dfs2(to[i]);
}
}
int main(){
scanf("%d%d%d",&n,&m,&q);
for(int i=;i<=n;i++) fa[i]=i;
for(int i=;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=;i<=n;i++)
if(find(i)==i){
int src;src=i;
memset(dis,-,sizeof(dis));
dis[src]=;dfs(src);
for(int j=;j<=n;j++)
if(dis[j]>dis[src]) src=j;
memset(dis,-,sizeof(dis));
dis[src]=;dfs(src);lcnt[i]=src;
for(int j=;j<=n;j++)
if(dis[j]>dis[src]) src=j;
rcnt[i]=src;int minn=0x7f7f7f7f;
dfs1(src);memset(dad,,sizeof(dad));
dfs2(src);memset(dad,,sizeof(dad));
for(int j=;j<=n;j++)
if(find(j)==i){
int len=abs(sum1[j]-sum2[j]);
if(len<minn){ minn=len;mid[i]=j; }
}
memset(sum1,,sizeof(sum1));
memset(sum2,,sizeof(sum2));
}
for(int i=;i<=q;i++){
int opt,x,y;
scanf("%d%d",&opt,&x);
if(opt==){
memset(dis,-,sizeof(dis));
dis[x]=;dfs(x);int dx=find(x);
int ans=max(dis[lcnt[dx]],dis[rcnt[dx]]);
int maxn=,ops;
if(dis[lcnt[dx]]>dis[rcnt[dx]]) ops=lcnt[dx];
else ops=rcnt[dx];
dfs1(lcnt[dx]);memset(dad,,sizeof(dad));
dfs2(rcnt[dx]);memset(dad,,sizeof(dad));
for(int j=;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,,sizeof(sum1));
memset(sum2,,sizeof(sum2));
}
else if(opt==){
scanf("%d",&y);
int dx=find(x),dy=find(y);
if(dx==dy) continue;fa[dy]=dx;
add(mid[dx],mid[dy]);
memset(dis,-,sizeof(dis));
int src;src=x;
dis[src]=;dfs(src);
for(int j=;j<=n;j++)
if(dis[j]>dis[src]) src=j;
memset(dis,-,sizeof(dis));
dis[src]=;dfs(src);lcnt[dx]=src;
for(int j=;j<=n;j++)
if(dis[j]>dis[src]) src=j;
rcnt[dx]=src;int minn=0x7f7f7f7f;
dfs1(lcnt[dx]);memset(dad,,sizeof(dad));
dfs2(rcnt[dx]);memset(dad,,sizeof(dad));
for(int j=;j<=n;j++)
if(find(j)==dx){
int len=abs(sum1[j]-sum2[j]);
if(len<minn){ minn=len;mid[dx]=j; }
}
memset(sum1,,sizeof(sum1));
memset(sum2,,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*],net[MAXN*],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]]==-){
dis[to[i]]=dis[now]+;
dfs(to[i]);
}
}
int main(){
scanf("%d%d%d",&n,&m,&q);
for(int i=;i<=n;i++) fa[i]=i;
for(int i=;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=;i<=n;i++)
if(find(i)==i){
int src;src=i; memset(dis,-,sizeof(dis));
dis[src]=;dfs(src);
for(int j=;j<=n;j++)
if(dis[j]>dis[src]) src=j;
lcnt[i]=src; memset(dis,-,sizeof(dis));
dis[src]=;dfs(src);
for(int j=;j<=n;j++)
if(dis[j]>dis[src]) src=j;
rcnt[i]=src;len[i]=dis[src];
}
for(int i=;i<=q;i++){
int opt,x,y;
scanf("%d%d",&opt,&x);
if(opt==) cout<<len[find(x)]<<endl;
else if(opt==){
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]+)/+(len[dy]+)/+;
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*],net[MAXN*],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=;
que.push(tmp);int maxn=,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+;
que.push(tmx);
if(tmx.dis>maxn){ maxn=tmx.dis;opt=to[i]; }
}
}
while(!que.empty()) que.pop();
tmp.pos=opt;tmp.dis=;
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+;
que.push(tmx);
if(tmx.dis>maxn){ maxn=tmx.dis; }
}
}
return maxn;
}
int main(){
scanf("%d%d%d",&n,&m,&q);
for(int i=;i<=n;i++) fa[i]=i;
for(int i=;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=;i<=n;i++)
if(find(i)==i) len[i]=bfs(i);
for(int i=;i<=q;i++){
int opt,x,y;
scanf("%d%d",&opt,&x);
if(opt==) cout<<len[find(x)]<<endl;
else if(opt==){
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]+)/+(len[dy]+)/+;
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分的代码

洛谷 P2195 HXY造公园的更多相关文章

  1. 洛谷 P2195 HXY造公园 解题报告

    P2195 HXY造公园 题目描述 现在有一个现成的公园,有\(n\)个休息点和\(m\)条双向边连接两个休息点.众所周知,\(HXY\)是一个\(SXBK\)的强迫症患者,所以她打算施展魔法来改造公 ...

  2. 【luogu P2195 HXY造公园】 题解

    题目链接:https://www.luogu.org/problemnew/show/P2195 fir.吐槽题目(省略1w字 sec.考虑对一个森林的维护,每棵树用并查集维护. 操作1:输出当前查询 ...

  3. 洛谷 P2194 HXY烧情侣【Tarjan缩点】 分析+题解代码

    洛谷 P2194 HXY烧情侣[Tarjan缩点] 分析+题解代码 题目描述: 众所周知,HXY已经加入了FFF团.现在她要开始喜(sang)闻(xin)乐(bing)见(kuang)地烧情侣了.这里 ...

  4. 【题解】洛谷P3953 [NOIP2017TG] 逛公园(记忆化搜索+SPFA)

    题目来源:洛谷P3953 思路 先用SPFA求一遍最短路 在求最短路的同时可以把所有点到终点的最短路求出来 dis数组 注意要反向SPFA  因为从起点开始可能会走到一些奇怪的路上导致时间负责度增加 ...

  5. 洛谷 P4145 上帝造题的七分钟2 / 花神游历各国

    洛谷 这题就是区间开根号,区间求和.我们可以分块做. 我们记布尔数组vis[i]表示第i块中元素是否全部为1. 因为显然当一个块中元素全部为1时,并不需要对它进行根号操作. 我们每个块暴力开根号,因为 ...

  6. 题解 P2195 【HXY造公园】

    天哪这道题竟然只有一篇题解! emm,首先读题看完两个操作就已经有很明确的思路了,显然是并查集+树的直径 一波解决. 并查集不多说了,如果不了解的可以看这里. 树的直径的思路很朴实,就是两边DFS(B ...

  7. 洛谷 P4074 [WC2013]糖果公园 解题报告

    P4074 [WC2013]糖果公园 糖果公园 树上待修莫队 注意一个思想,dfn序处理链的方法,必须可以根据类似异或的东西,然后根据lca分两种情况讨论 注意细节 Code: #include &l ...

  8. 洛谷 P4513 小白逛公园-区间最大子段和-分治+线段树区间合并(单点更新、区间查询)

    P4513 小白逛公园 题目背景 小新经常陪小白去公园玩,也就是所谓的遛狗啦… 题目描述 在小新家附近有一条“公园路”,路的一边从南到北依次排着nn个公园,小白早就看花了眼,自己也不清楚该去哪些公园玩 ...

  9. CF455C Civilization | luogu HXY造公园

    题目链接: https://www.luogu.org/problemnew/show/P2195 http://codeforces.com/contest/455/problem/C 显然我们可以 ...

随机推荐

  1. 湖南集训Day1

    难度 不断网:☆☆☆ 断网:☆☆☆☆ /* 卡特兰数取模 由于数据范围小,直接做. 考试时断网.忘记卡特兰数公式,推错了只有5分. 数学公式要记别每次都现用现搜!!! */ #include<i ...

  2. Gym - 100920H 2010-2011 OpenCup IX Onsite, II Yandex Summer School H.Squares 暴力

    题面 题意:有10w个点,问你选4个点,能组成平行于坐标轴的正方形有多少个 题解:不知道正解,我的做法就是暴力的基础上优化一点,每次按x排好序,每次枚举的2个点都是x相同的 这样算是个优化?但并不能过 ...

  3. IE下元素设置百分比的问题

    场景:近两天在做一个控件,该控件是一个tab型的,并且该tab有可能是两个tab标签,也有可能是多个tab标签,为了能够适应这种动态需求, 在设置标签宽度的时候,直接用的最外层容器除以tab的个数,然 ...

  4. C# System.Environment.GetFolderPath的使用 [转]

    原文:https://blog.csdn.net/yongyong521/article/details/75105853 获取系统文件目录 string strPath = Environment. ...

  5. linux 查看内存和cpu

    Linux查看CPU和内存使用情况 在系统维护的过程中,随时可能有需要查看 CPU 使用率,并根据相应信息分析系统状况的需要.在 CentOS 中,可以通过 top 命令来查看 CPU 使用状况.运行 ...

  6. js 中的定时器

    在js中的定时器分两种:1.setTimeout() 2.setInterval() 1.setTimeOut() 只在指定时间后执行一次 /定时器 异步运行 function hello(){ al ...

  7. Java常用类库(三) : HashSet和LinkedList特点简析

    今天内容: l  浅撩HashSet集合元素不可重复的原理 l  使用LinkedList模拟栈和队列 1.浅撩HashSet集合元素不可重复的原理 我们知道HashSet是添加不了相同的元素的,其原 ...

  8. 警告视图及操作表单在xcode7.0中的使用

    警告视图(alert)及操作表单(action sheet)都用于向用户提供反馈.(模态视图) 操作表单:要求用户在两个以上选项之间做出选择.操作表单从屏幕底部出现,显示一系列按钮供用户选择.用户必须 ...

  9. 从"嘿,今晚..."谈消息安全传输中的技术点

    一.初级阶段:信息裸传 特点:在网络上传递明文 黑客定理一:网络上传递的数据是不安全的,属网络于黑客公共场所,能被截取 结果:传递明文无异于不穿衣服裸奔 改进方案:先加密,再在网络上传输 二.进阶阶段 ...

  10. 常用的Axure操作方法(1)

    1. 保存原型图片到本地,如在网页上看到图标素材,好多个在一张图上.                                   如上图所示,将图片拖入axure中,利用分割或者裁剪,把小图标 ...