题目描述

现在有一个现成的公园,有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. [Swift通天遁地]二、表格表单-(2)创建右侧带有索引的UITableView(表单视图)

    ★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★➤微信公众号:山青咏芝(shanqingyongzhi)➤博客园地址:山青咏芝(https://www.cnblogs. ...

  2. [Swift通天遁地]二、表格表单-(7)电子邮件Mail:实现单元格左右滑动调出功能按钮

    ★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★➤微信公众号:山青咏芝(shanqingyongzhi)➤博客园地址:山青咏芝(https://www.cnblogs. ...

  3. centos6.6安装redis

    .安装仓库 yum install epel-release -y2.安装redis yum install redis -y3.程序文件说明 安装完毕后有以下几个文件位于/usr/bin目录: re ...

  4. SQL数据库还原的二种方式和区别

    1.数据库还原 在SQL中,直接选择选择“还原数据库”:选中.bak 文件即可. 2.生成脚本 新建同样的DB名字,在SQL打开脚本,执行脚本语言.数据库里面就会自动填充内容.

  5. Mac OS安装octave出现的问题-'error:terminal type set to 'unknown'的解决'

    学习Machine learning需要使用Octave语言,毕竟Andrew Ng (恩达.吴)力荐.本机系统Mac OS X EI Capitan, 其实什么系统都无所谓了,安装原理都是一样的. ...

  6. cloudera-scm-server启动时出现Caused by: java.io.FileNotFoundException: /var/lib/cloudera-scm-server/.keystore (No such file or directory)问题解决方法(图文详解)

    不多说,直接上干货! 问题详情 查看/var/log/cloudera-scm-server.log的启动日志 问题来源 我在用cloudermanager安装好之后,然后,在对如下. 配置kerbe ...

  7. iOS - UITableView 多选功能实现

    :自定义Cell中的代码 #import <UIKit/UIKit.h> @interface TestCell : UITableViewCell @property(nonatomic ...

  8. php中的抽象方法和抽象类

    1.什么是抽象方法?    我们在类里面定义的没有方法提的方法就是抽象方法.所谓的没有方法体指的是,在声明的时候没有大括号以及其中的内容,而是直接在声明时在方法名后加上分号结束,另外在声明抽象方法时方 ...

  9. java编码终极探秘

    首先要明白,java中string字符串都是unicode码保存的,只不过显示的时候会根据一定的规则,比如GBK或者是UTF-8去对照表中查找进行显示. 之所以会乱码就是因为使用错了编码方式. 数据是 ...

  10. How an SSL connection is established

    An SSL connection between a client and server is set up by a handshake, the goals of which are: To s ...