http://acm.hdu.edu.cn/showproblem.php?pid=6165
坑啊,我以前的模板是树上的tarjan,
给定一个图,要求是否存在两个点不能到达(任意一个到达任意一个都算到达)。如果存在,就输出那个长长的矫情的情话。
否则就输出那个短短的情话。
边是有向边。因为题目说了不能返回
没有找到一个 scc时,没有把栈里的标记都给置0.。
然后我就感觉那么多人都过了,不应该是这个玩意,毕竟他比较冷门。。。
修正了 模板,还有就是这个题稍微麻烦一点,得重新见图缩点。
。。缩点后 拓扑我已经想到了(因为他要两者任意就行,我就想到了用拓扑关系表示),但是当时就是模板错了qwq。。
wa了几次,scc没有 置0.。
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <stack>
#include <vector>
#include <cstring>
#include <bits/stdc++.h>
/* 坑啊,以前没有好好准备tarjan模板,我的模板是树上用的,
不够专业,所以图上开始用就错了,。
并且当时有点乱,我感觉很多人都写了,肯定不是tarjan了。
结果还真是。。
*/
using namespace std;
const int maxn=500000;
int dfn[maxn];//dfs顺序。和一种求lca的序不一样??
int low[maxn];//最小能够到达的点。
int index1;//记录时间的标号
bool state[maxn];//是否在栈里.
stack<int>s;
vector<int>G[maxn];
vector<int>g[maxn];
int cnt[maxn];
int num[maxn];
int scc;
int vis[maxn];
void tarjan(int u)// 万能板 tarjan 处理强连通分量。
{ dfn[u]=low[u]=++index1;//
s.push(u);
state[u]=true;
for(int i=0;i<G[u].size();i++){
int w=G[u][i];
if(!dfn[w]){
tarjan(w);
low[u]=min(low[w],low[u]);
}
else if(state[w])
low[u]=min(low[u],dfn[w]);//在次遇见你。。
}
if(low[u]==dfn[u])
{
scc++;
for(;;)
{
int x = s.top();s.pop();
cnt[x]=scc;
num[scc]++;
state[x]=false;//其实就是少了这一句。。。
if(x==u)break;
}
}
}
int main()
{ //freopen("f:\\ttt\\1005.txt","r",stdin);
//freopen("f:\\ttt\\out1.txt","w",stdout);
int t;
int a,m,b;
int ttt;
int du[maxn];
scanf("%d",&ttt);
while(ttt--){
for(int i=0;i<maxn;i++){
G[i].clear();
g[i].clear();
}
scanf("%d%d",&t,&m);
memset(du,0,sizeof(du));
for(int i=1;i<=m;i++){
scanf("%d%d",&a,&b);
G[a].push_back(b);
}
scc=0;
memset(state,false,sizeof(state));
memset(dfn,0,sizeof(dfn));
memset(low,0,sizeof(low));
memset(cnt,0,sizeof(cnt));
memset(vis,false,sizeof(vis));
memset(num,0,sizeof(num));
while(!s.empty())
s.pop();
for(int i=1;i<=t;i++)
if(!dfn[i]){
//memset(vis,false,sizeof(vis));
tarjan(i);
}
for(int i=1;i<=t;i++){
a=cnt[i];
for(int j=0;j<G[i].size();j++){
b=cnt[G[i][j]];
if(a!=b){
g[a].push_back(b);
du[b]++;//有出度
//cout<<a<<"!!"<<b<<endl;
}
}
}
bool flag=false;
queue<int>qq;
while(!qq.empty()) qq.pop();
int sizz=0;
for(int i=1;i<=scc;i++){
//cout<<du[i]<<endl;
if(!du[i]){
qq.push(i);
sizz++;// 这个是判断 开始是否有两个,如果这样也是不行的。
}
}
if(sizz>=2) {
//cout<<"??"<<endl;
flag=true;}
while(!qq.empty()&&!flag){
int u=qq.front();
qq.pop();
int siz=0;
for(int i=0;i<g[u].size()&&!flag;i++){
int to=g[u][i];
du[to]--;
if(du[to]==0){
siz++;
qq.push(to);
}
if(siz>=2) flag=true;
}
if(flag) break;
}
if(flag)
puts("Light my fire!");
else
puts("I love you my love and our love save us!");
}
return 0;
}
又看了大佬的写法。暴力搜索每个点,因为题目说不能返回,在就创造一个正向图,一个反向图。然后每个点枚举搜索。。
为啥要正向反向呢,正向意味着他能到其他点,反向意味着其他点能到他,如果两种方法都到不了,说明他们没有缘分,只能此恨绵绵无绝期了。而如果有一种能到就算。。 大佬太强了,还是河南的,真是我河南之福
#include <bits/stdc++.h>
using namespace std;
/* 偶然看到大佬的做法,
我tm醉了。
直接搜索正向图,和反向图。
如果有不连接的,就不行。
*/
const int maxn=1e4+1000;
vector<int>G[maxn];
vector<int>v[maxn];
bool vis1[maxn];
bool vis2[maxn];
void dfs1(int u){
vis1[u]=true;
for(int i=0;i<G[u].size();i++){
int to=G[u][i];
if(!vis1[to]){
dfs1(to);
}
}
}
void dfs2(int u){
vis2[u]=true;
for(int i=0;i<v[u].size();i++){
int to=v[u][i];
if(!vis2[to]){
dfs2(to);
}
}
}
int main()
{ int t,m,n,a,b;
scanf("%d",&t);
while(t--){
for(int i=0;i<maxn;i++){
G[i].clear();
v[i].clear();
}
scanf("%d%d",&m,&n);
for(int i=0;i<n;i++){
scanf("%d%d",&a,&b);
G[a].push_back(b);
v[b].push_back(a);
}
bool flag=false;
for(int i=1;i<=m&&!flag;i++){
memset(vis1,false,sizeof(vis1));
memset(vis2,false,sizeof(vis2));
dfs1(i);
dfs2(i);
for(int i=1;i<=m&&!flag;i++){
if(!vis1[i]&&!vis2[i]){
flag=true;
}
}
}
if(!flag)
puts("I love you my love and our love save us!");
else
puts("Light my fire!");
}
return 0;
}