tarjan+拓扑序+差分--2018.10.16模拟赛T2

2024-01-03 14:18

本文主要是介绍tarjan+拓扑序+差分--2018.10.16模拟赛T2,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

题目:
阿天住的城市有 n 个政府部门,这些部门之间用了 m 条有向路径
连接了起来。有趣的是,每过一天这些路径都会变换方向,也就是说,
偶数的日子和奇数的日子的图是不同的。
阿天在社保局工作,可惜他过于丢人忘记了社保局的位置。他只
记得由于社保局很重要,它在一个可以到达所有其他部门的地点。请
你帮他找到所有满足条件的地点。

solution:
首先肯定要缩点,因为强连通分量不管怎样都会走到
然后考场上还想出了拓扑排序,但是处理方式不太对
我想的是分层然后如果这层只有一个点就可以,但之后举出了反例

正解应该是利用拓扑序和拓扑的性质
如果一个点可以到达的点和可以被到达的点加起来超过了 n n n那这个点就是答案点
考虑如何计算一个点 u u u可以到达哪些点,因为拓扑序有分层的特点
在去点 t o [ u ] to[u] to[u]中找到 i d [ v ] id[v] id[v]最小的,那么从 i d [ u ] + 1 id[u]+1 id[u]+1 i d [ v ] − 1 id[v]-1 id[v]1这一段都是不能被到达的,被到达和这个方法一样

就可以在拓扑序上差分然后前缀和,如果 s u m [ i ] = 0 sum[i]=0 sum[i]=0就是答案点

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<vector>
#define N 1000005
using namespace std;
int n,m,cnt,head[N],eh[N],ec,sum[N],num,id[N],pos[N],pre[N];
int dfn[N],low[N],stk[N],tot,top,tim,bel[N],d[N];
bool vis[N],can[N];
vector<int> scc[N],ans;inline int rd(){int x=0,f=1;char c=' ';while(c<'0' || c>'9') f=c=='-'?-1:1,c=getchar();while(c<='9' && c>='0') x=x*10+c-'0',c=getchar();return x*f; 
}struct EDGE{int to,nxt; 
}edge[N],e[N];
inline void add(int x,int y){edge[++cnt].to=y; edge[cnt].nxt=head[x]; head[x]=cnt;
}
inline void add2(int x,int y){e[++ec].to=y; e[ec].nxt=eh[x]; eh[x]=ec; ++d[y];
}void tarjan(int u){dfn[u]=low[u]=++tim; vis[u]=1; stk[++top]=u;for(int i=head[u];i;i=edge[i].nxt){int v=edge[i].to;if(!dfn[v]){tarjan(v);low[u]=min(low[u],low[v]);}else if(vis[v]) low[u]=min(low[u],dfn[v]);}if(dfn[u]==low[u]){tot++; int tmp;do{tmp=stk[top--]; vis[tmp]=0;bel[tmp]=tot; scc[tot].push_back(tmp);}while(u!=tmp);} return;
}inline void rebuild(){for(int u=1;u<=n;u++)for(int i=head[u];i;i=edge[i].nxt){int v=edge[i].to;if(bel[v]!=bel[u]) add2(bel[u],bel[v]);}
}inline void solve(){top=0;for(int i=1;i<=tot;i++){can[i]=true;if(!d[i]) stk[++top]=i,id[i]=++num,pos[num]=i;}while(top){int u=stk[top--];for(int i=eh[u];i;i=e[i].nxt){int v=e[i].to;--d[v];if(!d[v]) stk[++top]=v,id[v]=++num,pos[num]=v;}}for(int u=1;u<=tot;u++)for(int i=eh[u];i;i=e[i].nxt){int v=e[i].to;pre[v]=max(pre[v],id[u]);//找到连到v的id最大的点 }for(int i=1;i<=num;i++){int u=pos[i];sum[pre[u]+1]++,sum[i]--;}for(int i=1;i<=num;i++){pre[i]=tot+1; sum[i]+=sum[i-1];if(sum[i]) can[pos[i]]=false;}memset(sum,0,sizeof sum);for(int u=1;u<=tot;u++)for(int i=eh[u];i;i=e[i].nxt){int v=e[i].to;pre[u]=min(pre[u],id[v]);//找到u连接的id最小的点 }for(int i=1;i<=num;i++){int u=pos[i];sum[pre[u]-1]++,sum[i]--;}for(int i=num;i;i--){sum[i]+=sum[i+1];if(sum[i]) can[pos[i]]=false;}
}int main(){freopen("wtmsb.in","r",stdin);freopen("wtmsb.out","w",stdout);n=rd(); m=rd();for(int i=1;i<=m;i++){int x=rd(),y=rd();add(x,y);}for(int i=1;i<=n;i++)if(!dfn[i]) tarjan(i);rebuild(); solve();for(int i=1;i<=tot;i++)if(can[i])for(int j=0;j<scc[i].size();j++)ans.push_back(scc[i][j]);sort(ans.begin(),ans.end());printf("%d\n",ans.size());for(int i=0;i<ans.size();i++)printf("%d ",ans[i]);return 0;
}

这篇关于tarjan+拓扑序+差分--2018.10.16模拟赛T2的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/565969

相关文章

【C++】_list常用方法解析及模拟实现

相信自己的力量,只要对自己始终保持信心,尽自己最大努力去完成任何事,就算事情最终结果是失败了,努力了也不留遗憾。💓💓💓 目录   ✨说在前面 🍋知识点一:什么是list? •🌰1.list的定义 •🌰2.list的基本特性 •🌰3.常用接口介绍 🍋知识点二:list常用接口 •🌰1.默认成员函数 🔥构造函数(⭐) 🔥析构函数 •🌰2.list对象

usaco 1.2 Transformations(模拟)

我的做法就是一个一个情况枚举出来 注意计算公式: ( 变换后的矩阵记为C) 顺时针旋转90°:C[i] [j]=A[n-j-1] [i] (旋转180°和270° 可以多转几个九十度来推) 对称:C[i] [n-j-1]=A[i] [j] 代码有点长 。。。 /*ID: who jayLANG: C++TASK: transform*/#include<

poj 3159 (spfa差分约束最短路) poj 1201

poj 3159: 题意: 每次给出b比a多不多于c个糖果,求n最多比1多多少个糖果。 解析: 差分约束。 这个博客讲差分约束讲的比较好: http://www.cnblogs.com/void/archive/2011/08/26/2153928.html 套个spfa。 代码: #include <iostream>#include <cstdio>#i

hdu 1285(拓扑排序)

题意: 给各个队间的胜负关系,让排名次,名词相同按从小到大排。 解析: 拓扑排序是应用于有向无回路图(Direct Acyclic Graph,简称DAG)上的一种排序方式,对一个有向无回路图进行拓扑排序后,所有的顶点形成一个序列,对所有边(u,v),满足u 在v 的前面。该序列说明了顶点表示的事件或状态发生的整体顺序。比较经典的是在工程活动上,某些工程完成后,另一些工程才能继续,此时

poj 3169 spfa 差分约束

题意: 给n只牛,这些牛有些关系。 ml个关系:fr 与 to 牛间的距离要小于等于 cost。 md个关系:fr 与 to 牛间的距离要大于等于 cost。 隐含关系: d[ i ] <= d[ i + 1 ] 解析: 用以上关系建图,求1-n间最短路即可。 新学了一种建图的方法。。。。。。 代码: #include <iostream>#include

hdu4431麻将模拟

给13张牌。问增加哪些牌可以胡牌。 胡牌有以下几种情况: 1、一个对子 + 4组 3个相同的牌或者顺子。 2、7个不同的对子。 3、13幺 贪心的思想: 对于某张牌>=3个,先减去3个相同,再组合顺子。 import java.io.BufferedInputStream;import java.io.BufferedReader;import java.io.IOExcepti

POJ 1364差分约束

给出n个变量,m个约束公式 Sa + Sa+1 + .... + Sa+b < ki or > ki ,叫你判断是否存在着解满足这m组约束公式。 Sa + Sa+1   +   .+ Sa+b =  Sum[a+b] - Sum[a-1]  . 注意加入源点n+1 。 public class Main {public static void main(Strin

【每日一题】LeetCode 2181.合并零之间的节点(链表、模拟)

【每日一题】LeetCode 2181.合并零之间的节点(链表、模拟) 题目描述 给定一个链表,链表中的每个节点代表一个整数。链表中的整数由 0 分隔开,表示不同的区间。链表的开始和结束节点的值都为 0。任务是将每两个相邻的 0 之间的所有节点合并成一个节点,新节点的值为原区间内所有节点值的和。合并后,需要移除所有的 0,并返回修改后的链表头节点。 思路分析 初始化:创建一个虚拟头节点

每日一题|牛客竞赛|四舍五入|字符串+贪心+模拟

每日一题|四舍五入 四舍五入 心有猛虎,细嗅蔷薇。你好朋友,这里是锅巴的C\C++学习笔记,常言道,不积跬步无以至千里,希望有朝一日我们积累的滴水可以击穿顽石。 四舍五入 题目: 牛牛发明了一种新的四舍五入应用于整数,对个位四舍五入,规则如下 12345->12350 12399->12400 输入描述: 输入一个整数n(0<=n<=109 ) 输出描述: 输出一个整数

【JavaScript】LeetCode:16-20

文章目录 16 无重复字符的最长字串17 找到字符串中所有字母异位词18 和为K的子数组19 滑动窗口最大值20 最小覆盖字串 16 无重复字符的最长字串 滑动窗口 + 哈希表这里用哈希集合Set()实现。左指针i,右指针j,从头遍历数组,若j指针指向的元素不在set中,则加入该元素,否则更新结果res,删除集合中i指针指向的元素,进入下一轮循环。 /*** @param