HZOI20190725 B 回家 tarjan
题目大意:https://www.cnblogs.com/Juve/articles/11226266.html
题解:
感觉挺水的,但考场上没打出来
题目翻译一下就是输出起点到终点必经的点
其实就是求起点到终点的割点
这题算法非常多,我介绍我的方法:
55%:
可能这个算法是不严谨的,但至少能那55分
我们先tarjan求点双,然后缩点
然后我们从1号点所在点双开始dfs,
维护一个栈,每到一个点双,就把点双中的割点压入栈里,继续搜索
回溯时在弹栈,当我们搜到n号点所在点双,就停止搜索
这样栈中剩余的点就是我们要求的点
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<string>
#include<vector>
#include<stack>
#define MAXM 4000005
#define MAXN 2000005
#define ll long long
#define re register
using namespace std;
inline ll read(){
re ll x=0;char ch=getchar();
while(ch<'0'||ch>'9'){ch=getchar();}
while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}
return x;
}
ll t,n,m;
ll to[MAXM<<2],nxt[MAXM<<2],pre[MAXN],cnt=0;
inline void add(re ll u,re ll v){
cnt++,to[cnt]=v,nxt[cnt]=pre[u],pre[u]=cnt;
}
ll ver[MAXM<<2],nxtt[MAXM<<2],head[MAXN],sum=0;
inline void ADD(re ll u,re ll v){
sum++,ver[sum]=v,nxtt[sum]=head[u],head[u]=sum;
}
ll dfn[MAXN],low[MAXN],dfs_order=0,sta[MAXN],top=0,dcc_num=0,belong[MAXN];
bool is_cut[MAXN];
vector<ll>dcc[MAXN];
inline void tarjan(re ll x){
dfn[x]=low[x]=++dfs_order;
sta[++top]=x;
ll num=0;
for(re ll i=pre[x];i;i=nxt[i]){
re ll y=to[i];
if(!dfn[y]){
tarjan(y);
low[x]=min(low[x],low[y]);
if(low[y]>=dfn[x]){
num++,dcc_num++;
if(x!=1||num>1) is_cut[x]=1;
re ll z;
do{
z=sta[top--];
dcc[dcc_num].push_back(z);
}while(y!=z);
dcc[dcc_num].push_back(x);
}
}
else low[x]=min(low[x],dfn[y]);
}
}
stack<ll>a;
ll tot=0,num=0,new_id[MAXN];
bool flag=0,ans[MAXN];
void dfs(ll x,ll fa){
if(x==belong[n]){
flag=1;
return ;
}
for(ll i=head[x];i;i=nxtt[i]){
if(ver[i]==fa) continue;
ll y=ver[i],N=dcc[y].size();
for(ll j=0;j<N;j++){
if(is_cut[dcc[y][j]]){
//cout<<dcc[y][j]<<endl;
a.push(dcc[y][j]);
}
}
dfs(y,x);
if(flag) return ;
}
//cout<<a.top()<<endl;
if(!a.empty()) a.pop();
}
signed main(){
t=read();
while(t--){
n=read(),m=read();
for(re ll i=1,u,v;i<=m;i++){
u=read(),v=read();
if(u==v) continue;
add(u,v),add(v,u);
}
tarjan(1);
num=dcc_num;
for(re ll i=1;i<=n;i++)
if(is_cut[i]) new_id[i]=++num;
for(re ll i=1;i<=dcc_num;i++){
re ll N=dcc[i].size();
for(re ll j=0;j<N;j++){
re ll t=dcc[i][j];
if(is_cut[t]){
ADD(i,new_id[t]);
ADD(new_id[t],i);
belong[t]=new_id[t];
}else belong[t]=i;
}
}
//for(ll i=1;i<=n;i++)
// cout<<i<<' '<<belong[i]<<endl;
//cout<<"------------------------"<<endl;
//for(ll i=1;i<=num;i++){
// for(ll j=head[i];j;j=nxtt[j]){
// cout<<i<<' '<<ver[j]<<endl;
// }
//}
dfs(belong[1],0);
while(!a.empty()){
re ll tp=a.top();
a.pop();
if(!ans[tp]){
if(tp!=1&&tp!=n){
ans[tp]=1;
tot++;
}
}
}
printf("%lld\n",tot);
for(ll i=2;i<n;i++)
if(ans[i])
printf("%lld ",i);
puts("");
memset(pre,0,sizeof(pre));
cnt=dfs_order=tot=dcc_num=sum=num=0;
memset(dfn,0,sizeof(dfn));
memset(is_cut,0,sizeof(is_cut));
memset(dcc,0,sizeof(dcc));
memset(head,0,sizeof(head));
memset(ans,0,sizeof(ans));
flag=0;
}
return 0;
}
/*
15 19
1 2
1 3
1 12
12 13
13 14
12 14
2 4
3 4
4 6
4 5
6 5
5 7
5 8
7 8
7 9
8 9
9 15
15 11
15 10 4 3
1 2
2 3
3 4 5 5
1 2
2 3
3 4
4 5
4 1 5 5
1 2
2 3
3 4
4 5
5 1
*/
这有什么问题吗?为什么一直55分?有大佬能解释的话可以留言
另一种算法:
这个算法一直90分,但找不到问题(是某大佬提出的)
我们用dijkstra或spfa跑1到n的最短路,顺便记录路径
那么必经点一定在这条路上
那么我们就可以求桥,桥两边的点都是要求的点
100%:
其实只要在tarjan就割点的板子上加一句话就A了:
在判割点时,在判断条件后加上dfn[to[i]]<=dfn[n]就可以了
if(low[y]>=dfn[x]&&dfn[y]<=dfn[n]){
num++;
if(x!=||num>) is_cut[x]=,tot++;
}
想一下tarjan的实现原理,dfn[n]是终点的dfn值
我们发现如果一个节点y的dfn值大于n,那么n肯定优先于y被搜到,
而我们是从1开始dfs,所以y肯定不在1到n的必经路上
那么这道题就非常水了
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<string>
#define MAXM 400005
#define MAXN 200005
#define ll long long
#define re register
using namespace std;
inline ll read(){
re ll x=0;char ch=getchar();
while(ch<'0'||ch>'9'){ch=getchar();}
while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}
return x;
}
ll t,n,m;
ll to[MAXM<<1],nxt[MAXM<<1],pre[MAXN],cnt=0;
inline void add(re ll u,re ll v){
cnt++,to[cnt]=v,nxt[cnt]=pre[u],pre[u]=cnt;
}
ll dfn[MAXN],low[MAXN],dfs_order=0,tot=0;
bool is_cut[MAXN];
inline void tarjan(re ll x){
dfn[x]=low[x]=++dfs_order;
re ll num=0;
for(re ll i=pre[x];i;i=nxt[i]){
re ll y=to[i];
if(!dfn[y]){
tarjan(y);
low[x]=min(low[x],low[y]);
if(low[y]>=dfn[x]&&dfn[y]<=dfn[n]){
num++;
if(x!=1||num>1) is_cut[x]=1,tot++;
}
}
else low[x]=min(low[x],dfn[y]);
}
}
signed main(){
t=read();
while(t--){
n=read(),m=read();
for(re ll i=1,u,v;i<=m;i++){
u=read(),v=read();
if(u==v) continue;
add(u,v),add(v,u);
}
tarjan(1);
printf("%lld\n",tot);
for(re ll i=1;i<=n;i++){
if(is_cut[i])
printf("%lld ",i);
}
puts("");
cnt=dfs_order=tot=0;
memset(pre,0,sizeof(pre));
memset(dfn,0,sizeof(dfn));
memset(is_cut,0,sizeof(is_cut));
}
return 0;
}
再来一道loj上类似的题:ZJOI 2004「一本通 3.6 练习 2」嗅探器https://loj.ac/problem/10101
你会发现这道题非常水,只不过起点终点不是1和n了
代码:
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <string>
#define MAXM 2005
#define MAXN 1005
#define ll long long
#define re register
using namespace std;
ll a, b, n, u, v, ans = 0x7fffffff;
ll to[MAXM << 1], nxt[MAXM << 1], pre[MAXN], cnt = 0;
inline void add(re ll u, re ll v) { cnt++, to[cnt] = v, nxt[cnt] = pre[u], pre[u] = cnt; }
ll dfn[MAXN], low[MAXN], dfs_order = 0, tot = 0;
bool is_cut[MAXN];
inline void tarjan(re ll x) {
dfn[x] = low[x] = ++dfs_order;
re ll num = 0;
for (re ll i = pre[x]; i; i = nxt[i]) {
re ll y = to[i];
if (!dfn[y]) {
tarjan(y);
low[x] = min(low[x], low[y]);
if (low[y] >= dfn[x] && dfn[y] <= dfn[b]) {
num++;
if (x != a || num > 1)
tot++, ans = min(ans, x);
}
} else
low[x] = min(low[x], dfn[y]);
}
}
signed main() {
scanf("%lld", &n);
while (~scanf("%lld%lld", &u, &v)) {
if (u + v == 0)
break;
add(u, v), add(v, u);
}
scanf("%lld%lld", &a, &b);
tarjan(a);
if (tot)
printf("%lld\n", ans);
else
puts("No solution");
return 0;
}
HZOI20190725 B 回家 tarjan的更多相关文章
- NOIP 模拟七 考试总结
T1匹配 签到大水题,这里有hash,kmp,ac自动机,还有后缀数组,后缀自动机任您挑选. 不过这个数据范围有些坑啊,re就很不爽.做法我还是比较倾向hash的,毕竟不论神魔字符算法,hash大都能 ...
- 【模拟7.25】回家(tarjan V-DCC点双连通分量的求法及缩点 求割点)模板题
作为一道板子题放在第二题令人身心愉悦,不到一个小时码完连对拍都没打. 关于tarjan割点的注意事项: 1.在该板子中我们求的是V-DCC,而不是缩点,V-DCC最少有两个点组成,表示出掉一个块里的任 ...
- hihoCoder 1185 连通性·三(Tarjan缩点+暴力DFS)
#1185 : 连通性·三 时间限制:10000ms 单点时限:1000ms 内存限制:256MB 描述 暑假到了!!小Hi和小Ho为了体验生活,来到了住在大草原的约翰家.今天一大早,约翰因为有事要出 ...
- 2-sat(tarjan算法)hdu(1824)
hdu1824 Let's go home Time Limit: 10000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Othe ...
- HDU 1824 Let's go home(2-SAT+Tarjan)
Let's go home Time Limit: 10000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others) T ...
- noip模拟7[匹配·回家·寿司]
这次考试状态好像还是没有回来,只拿了55pts,还全是第一题的功劳,就是一个小KMP,然后还让我给打错了 就很难受,while打成了if,然后wa掉45分考完立马拿回来了,悔死了,害 第二题爆零了,为 ...
- NOIP模拟测试8「匹配·回家」
匹配 哈希能A 水到爆炸 回家 事实上我做过一个原题,甚至比这个回家难的多,而且那个题多组询问必经点 然后我做一组询问就打炸了 大约就是删了很多东西,然后自己想的太简单了 直接统计了割点,懒得打lca ...
- flhs笔试题-回家上机实践
这是最近参加的一个公司的笔试题,回家上机写了下代码,希望对有需要的小伙伴有用,简单实现字符串和数组在指定位置的插入: package org.flhs; import com.google.commo ...
- 三石推荐!把 Bootstrap 小清新带回家!
无敌传送门:http://fineui.com/demo_pro/default.aspx?theme=bootstrap1&menu=accordion 喜欢就来赞一个! 把麻烦留给三石 ...
随机推荐
- 反射Reflection
using System; using System.Collections.Generic; using System.Linq; using System.Reflection;// <-- ...
- HDU-1226-超级密码-队列+广搜+大数取模
Ignatius花了一个星期的时间终于找到了传说中的宝藏,宝藏被放在一个房间里,房间的门用密码锁起来了,在门旁边的墙上有一些关于密码的提示信息: 密码是一个C进制的数,并且只能由给定的M个数字构成,同 ...
- 在Ubuntu中安装MySQL (转载)
MySQL在Linux Ubuntu中安装 本文使用的Linux是Ubuntu 12.04.2 LTS 64bit的系统,安装MySQL数据库软件包可以通过apt-get实现. 在Linux Ubun ...
- Spring Boot Server容器配置
参数配置容器 server.xx开头的是所有servlet容器通用的配置,server.tomcat.xx开头的是tomcat特有的参数,其它类似. 所有参数绑定配置类:org.springframe ...
- Mybatis使用Dao代码方式CURD
Mybatis 使用Dao代码方式进行增.删.改.查. 1.Maven的pom.xml <project xmlns="http://maven.apache.org/POM/4.0. ...
- 无法解析的外部符号 jpeg_std_error
1>dlib.lib(png_loader.obj) : error LNK2001: 无法解析的外部符号 png_set_sig_bytes 1>dlib.lib(png_loader. ...
- java 多项式
/****************************************************************************** * Compilation: javac ...
- js 阻止事件
event.stopPropagation();//阻止事件冒泡 ,可阻止父类事件的发生 event.preventDefault();//阻止默认行为 如A标签
- webstorm安装与破解
1.下载webstorm和补丁文件 链接:https://pan.baidu.com/s/1aiHxPExAbDCcHxKtB82_vg 提取码:jo07 链接:https://pan.baidu.c ...
- [JZOJ3235] 数字八
题目 题目大意 给你一个二维的图,其中.代表完好,*代表有缺陷. 现在要在图上刻一个数字\(8\),满足: 由两个矩形组成. 每个矩形中必须有空隙在内部,也就是说,至少为\(3*3\)的矩形. 上矩形 ...