题目描述

现在有一个现成的公园,有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. JavaScript中.和[]有什么区别?

    .与[]都可以用于读取或修改对象属性. <script> var myData={ name:"Adam", weather:"sunny", }; ...

  2. Akka源码分析-Remote-位置透明

    上一篇博客中,我们研究了remote模式下如何发消息给远程actor,其实无论如何,最终都是通过RemoteActorRef来发送消息的.另外官网也明确说明了,ActorRef是可以忽略网络位置的,这 ...

  3. Spinner的样式大致简介

    Spinner Spinner 是一个列表选择框,会在用户选择后,展示一个列表供用户进行选择.Spinner是ViewGroup的间接子类,它和其他的Android控件一样,数据需要使用Adapter ...

  4. Context的正确使用

    一.Context的作用 Context的最大作用就是我们可以通过传递它来获得其他Activity或Application的相关资源和方法,它就相当于它们的引用,我们通过引用来获得对象的封装,这也是我 ...

  5. unity3d 各键值对应代码

    KeyCode :KeyCode是由Event.keyCode返回的.这些直接映射到键盘上的物理键.  值        对应键 Backspace     退格键 Delete      Delet ...

  6. Sql Server 优化 SQL 查询:如何写出高性能SQL语句

    1. 首先要搞明白什么叫执行计划? 执行计划是数据库根据SQL语句和相关表的统计信息作出的一个查询方案,这个方案是由查询优化器自动分析产生的,比如一条SQL语句如果用来从一个 10万条记录的表中查1条 ...

  7. js 学习笔记---基本概念

    早已接触javascript多年之后,竟然还有这些概念混淆不清,毫不知情,说出来真实无地自容 ! 1.使用严格模式,"use strict",虽然不适用,但是要知道,以免别人使用时 ...

  8. Linux 配置JDK + MyEclipse

    版本:Ubuntu16.04: jdK: Java SE Development Kit 8u102; My Eclipse: 10.6; JDK配置的细致步骤参见此处. 就一点要注意: 请使用代码进 ...

  9. PHP 之获取Windows下CPU、内存的使用率

    <?php /** * Created by PhpStorm. * User: 25754 * Date: 2019/5/4 * Time: 13:42 */ class SystemInfo ...

  10. 创建全局函数 匹配查找 std::map

    std::map<CString, CString> m_NameToType; 所有文件之外声明一个函数 在要用到的地方  加入存储的东西 extern std::map<CStr ...