P2002 消息扩散
题目描述
有n个城市,中间有单向道路连接,消息会沿着道路扩散,现在给出n个城市及其之间的道路,问至少需要在几个城市发布消息才能让这所有n个城市都得到消息。
tarjan缩点(因为一个强连通分量内的点只要又一个传到了其他点都传到了,所以可以缩成一个点),然后重新建图,统计out[i]为0的点的数量
#include <iostream>
#include <algorithm>
#include <set>
#include <cstring>
using namespace std;
const int maxn = 1e5+5;
set<int> st[maxn];
struct Edge{
int u,v,nxt;
}edge[500005];
int head[maxn],tot;
inline void init(){
memset(head,-1,sizeof(head));
tot = 0;
}
inline void addedge(int u,int v){
edge[++tot] = {u,v,head[u]};
head[u] = tot;
}
bool vis[maxn];
int dfn[maxn],low[maxn],dfnnum,sta[maxn],stalen,num,color[maxn],out[maxn];
void tarjan(int u){
dfn[u] = low[u] = ++dfnnum;
sta[stalen++] = u,vis[u] = true;
for(int i = head[u]; ~i; i = edge[i].nxt){
Edge &e = edge[i];
if(!dfn[e.v]){
tarjan(e.v);
low[u] = min(low[u],low[e.v]);
}else if(vis[e.v]) low[u] = min(low[u],dfn[e.v]);
}
if(dfn[u]==low[u]){
++num; int cur;
do{
cur = sta[--stalen];
vis[cur] = false;
color[cur] = num;
}while(u!=cur);
}
}
int main()
{
init();
int n,m; cin >> n >> m;
for(int i = 0; i < m; ++i){
int u,v; cin >> u >> v;
if(u==v) continue;
addedge(u,v);
}
for(int i = 1; i <= n; ++i){
if(!dfn[i]) tarjan(i);
}
for(int i = 1; i <= n; ++i){
for(int j = head[i]; ~j; j = edge[j].nxt){
int u = color[i],v = color[edge[j].v];
if(u!=v && !st[u].count(v)){
out[v]++;
st[u].insert(v);
}
}
}
int ans = 0;
for(int i = 1; i <= num; ++i){
if(!out[i]) ans++;
}
cout << ans << endl;
return 0;
}
P2169 正则表达式
题目背景
小Z童鞋一日意外的看到小X写了一个正则表达式的高级程序,这个正则表达式程序仅仅由字符“0”,“1”,“.”和“*”构成,但是他能够匹配出所有在OJ上都AC的程序的核心代码!小Z大为颇感好奇,于是他决定入侵小X的电脑上去获得这个正则表达式的高级程序。
题目描述
在Internet网络中的每台电脑并不是直接一对一连通的,而是某些电脑之间存在单向的网络连接,也就是说存在A到B的连接不一定存在B到A的连接,并且有些连接传输速度很快,有些则很慢,所以不同连接传输所花的时间是有大有小的。另外,如果存在A到B的连接的同时也存在B到A的连接的话,那么A和B实际上处于同一局域网内,可以通过本地传输,这样花费的传输时间为0。
现在小Z告诉你整个网络的构成情况,他希望知道从他的电脑(编号为1),到小X的电脑(编号为n)所需要的最短传输时间。
很显然在同一局域网内的定义就是->强连通分量,那么我们将强连通分量内的点连边为0,其他的依然保持不变,然后跑一遍最短路即可
#include <iostream>
#include <queue>
#include <cstring>
#include <algorithm>
using namespace std;
const int maxn = 2e5+5;
const int N = 1e6+5;
const int inf = 0x3f3f3f3f;
struct Edge{
int u,v,nxt,w;
}edge[N],e[N];
int head[maxn],tot;
int Head[maxn],tot2;
inline void init(){
memset(head,-1,sizeof(head));
memset(Head,-1,sizeof(Head));
tot = 0;
}
inline void addedge(int u,int v,int w){
edge[++tot] = {u,v,head[u],w};
head[u] = tot;
}
inline void add(int u,int v,int w){
e[++tot2] = {u,v,Head[u],w};
Head[u] = tot2;
}
int dis[maxn]; bool tag[maxn];
struct Node{
int u,dis;
bool operator <(const Node &h)const{
return dis > h.dis;
}
};
inline int dijiastra(int s,int t,int n){
for(int i = 0; i <= n; ++i){
dis[i] = inf,tag[i] = false;
}
dis[s] = 0;
priority_queue<Node> pq; pq.push(Node{s,0});
while(!pq.empty()){
Node x = pq.top(); pq.pop();
if(tag[x.u]) continue;
tag[x.u] = true;
for(int i = Head[x.u]; ~i; i = e[i].nxt){
Edge &ee = e[i];
if(!tag[ee.v] && dis[ee.v] > dis[x.u] + e[i].w){
dis[ee.v] = dis[x.u] + e[i].w;
pq.push(Node{ee.v,dis[ee.v]});
}
}
}
return dis[t];
}
bool vis[maxn];
int dfn[maxn],low[maxn],cost[maxn],dfnnum,num,color[maxn],sta[maxn],stalen;
void tarjan(int u){
dfn[u] = low[u] = ++dfnnum;
sta[stalen++] = u,vis[u] = true;
for(int i = head[u]; ~i; i = edge[i].nxt){
Edge &e = edge[i];
if(!dfn[e.v]){
tarjan(e.v);
low[u] = min(low[u],low[e.v]);
}else if(vis[e.v]) low[u] = min(low[u],dfn[e.v]);
}
if(dfn[u]==low[u]){
++num; int cur;
do{
cur = sta[--stalen];
vis[cur] = false;
color[cur] = num;
}while(u!=cur);
}
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(0); cout.tie(0);
int n,m; cin >> n >> m;
init();
for(int i = 0; i < m; ++i){
int u,v,w; cin >> u >> v >> w;
addedge(u,v,w);
}
for(int i = 1; i <= n; ++i){
if(!dfn[i]) tarjan(i);
}
for(int i = 1; i <= n; ++i){
for(int j = head[i]; ~j; j = edge[j].nxt){
int u = color[i],v = color[edge[j].v];
if(u==v) add(i,edge[j].v,0);
else add(i,edge[j].v,edge[j].w);
}
}
cout << dijiastra(1,n,n) << endl;
return 0;
}
P2194 HXY烧情侣
题目描述
众所周知,HXY已经加入了FFF团。现在她要开始喜(sang)闻(xin)乐(bing)见(kuang)地烧情侣了。这里有n座电影院,n对情侣分别在每座电影院里,然后电影院里都有汽油,但是要使用它需要一定的费用。m条单向通道连接相邻的两对情侣所在电影院。然后HXY有个绝技,如果她能从一个点开始烧,最后回到这个点,那么烧这条回路上的情侣的费用只需要该点的汽油费即可。并且每对情侣只需烧一遍,电影院可以重复去。然后她想花尽可能少的费用烧掉所有的情侣。问最少需要多少费用,并且当费用最少时的方案数是多少?由于方案数可能过大,所以请输出方案数对1e9+7取模的结果。
(注:这里HXY每次可以从任何一个点开始走回路。就是说一个回路走完了,下一个开始位置可以任选。所以说不存在烧不了所有情侣的情况,即使图不连通,HXY自行选择顶点进行烧情侣行动。且走过的道路可以重复走。)
这条回路上的情侣-> 强连通分量,一个强连通分量内选择那个cost最小的点,然后方案数是每个连通分量内 cost最小的点的个数的乘积
#include <iostream>
#include <algorithm>
#include <vector>
using namespace std;
const int mo = 1e9+7;
const int maxn = 1e5+5;
const int inf = 0x3f3f3f3f;
int cost[maxn]; // the cost to fire
int head[maxn],tot,siz[maxn];
struct Edge{
int u,v,nxt;
}edge[300005];
inline void addedge(int u,int v){
edge[++tot] = {u,v,head[u]};
head[u] = tot;
}
bool vis[maxn];
int dfn[maxn],sta[maxn],low[maxn],stalen,dfnnum,mincost[maxn],color[maxn],num;
inline void init(int n){
for(int i = 0; i <= n; ++i){
head[i] = -1,dfn[i] = 0,low[i] = 0,mincost[i] = inf,color[i] = 0;
}
num = dfnnum = stalen = tot = 0;
}
void tarjan(int u){
dfn[u] = low[u] = ++dfnnum;
sta[stalen++] = u; vis[u] = true;
for(int i = head[u]; ~i; i = edge[i].nxt){
Edge &e = edge[i];
if(!dfn[e.v]){
tarjan(e.v);
low[u] = min(low[u],low[e.v]);
}else if(vis[e.v]) low[u] = min(low[u],dfn[e.v]);
}
if(dfn[u]==low[u]){
++num; int cur;
vector<int> vec;
do{
cur = sta[--stalen];
mincost[num] = min(mincost[num],cost[cur]);
vec.push_back(cost[cur]);
vis[cur] = false;
color[cur] = num;
}while(u!=cur);
for(auto i : vec){
if(i==mincost[num])
siz[num]++;
}
}
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(0); cout.tie(0);
int n; cin >> n;
init(n);
for(int i = 1; i <= n; ++i) cin >> cost[i];
int m; cin >> m;
for(int i = 0; i < m; ++i){
int u,v; cin >> u >> v;
addedge(u,v);
}
for(int i = 1; i <= n; ++i){
if(!dfn[i]) tarjan(i);
}
long long ans = 0,ans2 = 1;
for(int i = 1; i <= num; ++i){
ans += mincost[i];
ans2 = (1ll*ans2%mo*siz[i]%mo)%mo;
}
cout << ans << ' ' << ans2 << endl;
return 0;
}
P1262 间谍网络
#include <iostream>
#include <algorithm>
#include <cstring>
using namespace std;
const int maxn = 2e5+5;
const int inf = 0x3f3f3f3f;
int bribe[maxn];
struct Edge{
int u,v,nxt;
}edge[maxn];
int head[maxn],tot;
inline void addedge(int u,int v){
edge[++tot] = {u,v,head[u]};
head[u] = tot;
}
bool vis[maxn];
int dfn[maxn],low[maxn],sta[maxn],color[maxn],mincost[maxn],stalen,dfnnum,num;
int out[maxn];
inline void init(){
memset(head,-1,sizeof(head));
memset(mincost,0x3f,sizeof(mincost));
memset(bribe,0x3f,sizeof(bribe));
tot = 0;
}
void tarjan(int u){
dfn[u] = low[u] = ++dfnnum;
sta[stalen++] = u,vis[u] = true;
for(int i = head[u]; ~i; i = edge[i].nxt){
Edge &e = edge[i];
if(!dfn[e.v]){
tarjan(e.v);
low[u] = min(low[u],low[e.v]);
}else if(vis[e.v]) low[u] = min(low[u],dfn[e.v]);
}
if(dfn[u]==low[u]){
++num; int cur;
do{
cur = sta[--stalen];
vis[cur] = false;
mincost[num] = min(mincost[num],bribe[cur]);
color[cur] = num;
}while(u!=cur);
}
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(0); cout.tie(0);
init();
int n,p; cin >> n >> p;
for(int i = 0; i < p; ++i){
int u,m; cin >> u >> m;
bribe[u] = m; // stand for this people can be bribed;
}
int r; cin >> r;
for(int i = 0; i < r; ++i){
int u,v; cin >> u >> v;
addedge(u,v);
}
for(int i = 1; i <= n; ++i){
if(!dfn[i] && bribe[i]!=inf){
tarjan(i);
}
}
for(int i = 1; i <= n; ++i){
if(!dfn[i]){
cout << "NO" << endl << i << endl;
return 0;
}
}
for(int i = 1; i <= n; ++i){
for(int j = head[i]; ~j; j = edge[j].nxt){
int u = color[i],v = color[edge[j].v];
if(u!=v){
out[v]++;
}
}
}
int ans = 0;
for(int i = 1; i <= num; ++i){
if(!out[i]) ans += mincost[i];
}
cout << "YES" << endl;
cout << ans << endl;
return 0;
}
LA 4287 Proving Equivalences
命题互推(现在已经知道一些命题之间的推导关系)
问现在还需要证明多少个。
因为命题: a->b 可以抽象成有向图,如果命题之间等价的话,就说明两个命题之间彼此可以从 a->c 同时 c->a,这就是强连通的定义。
因此,先算出图中的强连通分量,然后缩点,之后找出入度为0的点的个数,选择最大的那个即是答案。