题目描述

现在有一个现成的公园,有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通天遁地]二、表格表单-(12)设置表单文字对齐方式以及自适应高度的文本区域TextArea

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

  2. Java并发编程系列之Semaphore详解

    简单介绍 我们以饭店为例,假设饭店只有三个座位,一开始三个座位都是空的.这时如果同时来了三个客人,服务员人允许他们进去用餐,然后对外说暂无座位.后来的客人必须在门口等待,直到有客人离开.这时,如果有一 ...

  3. python框架之Flask基础篇(二)-------- 数据库的操作

    1.flask连接数据库的四步: 倒入第三方数据库扩展包:from flask_sqlalchemy import SQLAlchemy 配置config属性,连接数据库: app.config[&q ...

  4. [转]Linux下/proc目录简介

    1. /proc目录Linux 内核提供了一种通过 /proc 文件系统,在运行时访问内核内部数据结构.改变内核设置的机制.proc文件系统是一个伪文件系统,它只存在内存当中,而不占用外存空间.它以文 ...

  5. JS——模拟百度搜索

    注意事项: 1.for循环移除子节点时,其长度是变化的 2.在文档流中,input.img.p等标签与其他标签有3px的距离,利用左浮动,可以消除3px距离 3.背景图片定位时,第一个值是x轴方向的值 ...

  6. (转)Hibernate关联映射——对象的三种关系

    http://blog.csdn.net/yerenyuan_pku/article/details/70148618 Hibernate关联映射——对象的三种关系 Hibernate框架基于ORM设 ...

  7. Python 之mysql类封装

    import pymysql class MysqlHelper(object): conn = None def __init__(self, host, username, password, d ...

  8. Java中接口与接口和类之间的关系

    接口和接口之间的关系 继承关系 可以多继承,并且可以多层继承 注意: 1.如果多个父接口中有同名的抽象方法,那么子接口只需要实现一次即可 2.如果多个父接口中有同名的默认方法,那么子接口必须重写默认方 ...

  9. %2d

    %2d是C语言中printf函数的输出格式说明符. 具体解释如下: 使输出的int型的数值以2位的固定位宽输出.如果不足2位,则在前面补空格:如果超过2位,则按实际位数输出. 注:如果输出的数值不是i ...

  10. 写一个 sum方法,在使用下面任一语法调用时,都可以正常工作

    console.log(sum(2,3)); // Outputs 5 console.log(sum(2)(3)); // Outputs 5 (至少)有两种方法可以做到: 方法1: functio ...