P2863 [USACO06JAN] The Cow Prom S
题目描述
有一个 n 个点,m 条边的有向图,请求出这个图点数大于 1 的强连通分量个数。
输入格式
第一行为两个整数 n 和 m。
第二行至 m+1 行,每一行有两个整数 a 和 b,表示有一条从 a 到 b 的有向边。
输出格式
仅一行,表示点数大于 1 的强连通分量个数。
输入输出样例
输入 #1复制
5 4
2 4
3 5
1 2
4 1
输出 #1复制
1
说明/提示
数据规模与约定
对于全部的测试点,保证 2≤n≤104,2≤m≤5×104,1≤a,b≤n。
实现代码:
cpp
#include<bits/stdc++.h>
#define maxn 10001
using namespace std;
vector<int>G[maxn];
stack<int>s;
int n,m;
int dfn[maxn],used[maxn],vis[maxn],low[maxn],color[maxn],num[maxn],colornum=0,cnt=0,ans=0;
void paint(int x)
{
s.pop();
color[x]=colornum;
num[colornum]++;
vis[x]=false;
}
void tarjan(int x)
{
dfn[x]=low[x]=++cnt;
s.push(x);
vis[x]=used[x]=true;
for(int i=0;i<G[x].size();i++)
{
int q=G[x][i];
if (!dfn[q])
{
tarjan(q);
low[x]=min(low[x],low[q]);
}
else if (vis[q]) low[x]=min(low[x],dfn[q]);
}
if (low[x]==dfn[x])
{
colornum++;
while(s.top()!=x)
{
int t=s.top();
paint(t);
}
paint(x);
}
}
int main()
{
cin>>n>>m;
for(int i=1;i<=m;i++)
{
int u,v;
cin>>u>>v;
G[u].push_back(v);
}
for(int i=1;i<=n;i++)
{
if (!used[i]) tarjan(i);
}
for(int i=1;i<=colornum;i++)
{
if (num[i]>1) ans++;
}
cout<<ans;
return 0;
}
P3387 【模板】缩点
题目描述
给定一个 n 个点 m 条边有向图,每个点有一个权值,求一条路径,使路径经过的点权值之和最大。你只需要求出这个权值和。
允许多次经过一条边或者一个点,但是,重复经过的点,权值只计算一次。
输入格式
第一行两个正整数 n,m。
第二行 n 个整数,其中第 i 个数 ai 表示点 i 的点权。
第三至 m+2 行,每行两个整数 u,v,表示一条 u→v 的有向边。
输出格式
共一行,最大的点权之和。
输入输出样例
输入 #1复制
2 2
1 1
1 2
2 1
输出 #1复制
2
说明/提示
对于 100% 的数据,1≤n≤104,1≤m≤105,0≤ai≤103。
- 2024-11-1 添加了 hack 数据;
实现代码:
cpp
#include<bits/stdc++.h>
using namespace std;
const int maxn=10000+15;
int n,m,sum,tim,top,s;
int p[maxn],head[maxn],sd[maxn],dfn[maxn],low[maxn];
int stac[maxn],vis[maxn];
int h[maxn],in[maxn],dist[maxn];
struct EDGE
{
int to;int next;int from;
}edge[maxn*10],ed[maxn*10];
void add(int x,int y)
{
edge[++sum].next=head[x];
edge[sum].from=x;
edge[sum].to=y;
head[x]=sum;
}
void tarjan(int x)
{
low[x]=dfn[x]=++tim;
stac[++top]=x;vis[x]=1;
for (int i=head[x];i;i=edge[i].next)
{
int v=edge[i].to;
if (!dfn[v]) {
tarjan(v);
low[x]=min(low[x],low[v]);
}
else if (vis[v])
{
low[x]=min(low[x],low[v]);
}
}
if (dfn[x]==low[x])
{
int y;
while (y=stac[top--])
{
sd[y]=x;
vis[y]=0;
if (x==y) break;
p[x]+=p[y];
}
}
}
int topo()
{
queue <int> q;
int tot=0;
for (int i=1;i<=n;i++)
if (sd[i]==i&&!in[i])
{
q.push(i);
dist[i]=p[i];
}
while (!q.empty())
{
int k=q.front();q.pop();
for (int i=h[k];i;i=ed[i].next)
{
int v=ed[i].to;
dist[v]=max(dist[v],dist[k]+p[v]);
in[v]--;
if (in[v]==0) q.push(v);
}
}
int ans=0;
for (int i=1;i<=n;i++)
ans=max(ans,dist[i]);
return ans;
}
int main()
{
scanf("%d%d",&n,&m);
for (int i=1;i<=n;i++)
scanf("%d",&p[i]);
for (int i=1;i<=m;i++)
{
int u,v;
scanf("%d%d",&u,&v);
add(u,v);
}
for (int i=1;i<=n;i++)
if (!dfn[i]) tarjan(i);
for (int i=1;i<=m;i++)
{
int x=sd[edge[i].from],y=sd[edge[i].to];
if (x!=y)
{
ed[++s].next=h[x];
ed[s].to=y;
ed[s].from=x;
h[x]=s;
in[y]++;
}
}
printf("%d",topo());
return 0;
}
P4782 【模板】2-SAT
题目描述
有 n 个布尔变量 x1∼xn,另有 m 个需要满足的条件,每个条件的形式都是 「xi 为 true / false 或 xj 为 true / false」。比如 「x1 为真或 x3 为假」、「x7 为假或 x2 为假」。
2-SAT 问题的目标是给每个变量赋值使得所有条件得到满足。
输入格式
第一行两个整数 n 和 m,意义如题面所述。
接下来 m 行每行 4 个整数 i, a, j, b,表示 「xi 为 a 或 xj 为 b」(a,b∈{0,1})
输出格式
如无解,输出 IMPOSSIBLE;
否则输出 POSSIBLE,下一行 n 个整数 x1∼xn(xi∈{0,1}),表示构造出的解。
输入输出样例
输入 #1复制
3 1
1 1 3 0
输出 #1复制
POSSIBLE
0 0 0
说明/提示
1≤n,m≤106 , 前 3 个点卡小错误,后面 5 个点卡效率。
由于数据随机生成,可能会含有 10 0 10 0 之类的坑,但按照最常规写法的写的标程没有出错,各个数据点卡什么的提示在标程里。
实现代码:
cpp
#include <cstdio>
#include <algorithm>
using std::min;
namespace IO_number{
int read(){
int x =0; char c =getchar(); bool f =0;
while(c < '0' || c > '9') (c == '-') ? f =1, c =getchar() : c =getchar();
while(c >= '0' && c <= '9') x =(x<<1)+(x<<3)+(48^c), c =getchar();
return (f) ? -x : x;
}
void write(const int &x){
if(x/10)
write(x/10);
putchar('0'+x%10);
}
}
using namespace IO_number;
const int MAXN =1e6+20;
int first[MAXN<<1], tote;
struct edge{
int to, nxt;
}e[MAXN<<1];
inline void addedge(const int &u, const int &v){
++tote, e[tote].to =v, e[tote].nxt =first[u], first[u] =tote;
}
int scc_id[MAXN<<1], Scc_id;
int dfn[MAXN<<1], low[MAXN<<1], Dfn;
int stk[MAXN<<1], stk_top;
bool instk[MAXN<<1];
void tarjan(const int &u){
low[u] =dfn[u] =++Dfn;
stk[stk_top++] =u;
instk[u] =1;
for(int l =first[u]; l; l =e[l].nxt){
if(!dfn[e[l].to]){
tarjan(e[l].to);
low[u] =min(low[u], low[e[l].to]);
}
else if(instk[e[l].to])
low[u] =min(low[u], dfn[e[l].to]);
}
if(low[u] == dfn[u]){
++Scc_id;
while(stk[stk_top] != u){
scc_id[stk[stk_top-1]] =Scc_id;
instk[stk[stk_top-1]] =0;
--stk_top;
}
}
}
int main(){
int n =read(), m =read();
for(int t =0; t < m; ++t){
int i =read(), a =read(), j =read(), b =read();
/* [1, n] -> false, [n+1, 2n] -> true */
// x_i, x_j 均不满足不能同时成立 //
addedge(i+(a^1)*n, j+b*n);
addedge(j+(b^1)*n, i+a*n);
}
for(int i =1; i <= 2*n; ++i){
if(!dfn[i])
tarjan(i);
if(i <= n && scc_id[i] == scc_id[i+n])
return puts("IMPOSSIBLE") && 0;
}
puts("POSSIBLE");
for(int i =1; i <= n; ++i)
write((scc_id[i+n] < scc_id[i])), putchar(' ');
}
P3469 [POI 2008] BLO-Blockade
题目描述
B 城有 n 个城镇(从 1 到 n 标号)和 m 条双向道路。
每条道路连结两个不同的城镇,没有重复的道路,所有城镇连通。
把城镇看作节点,把道路看作边,容易发现,整个城市构成了一个无向图。
请你对于每个节点 i 求出,把与节点 i 关联的所有边去掉以后(不去掉节点 i 本身),无向图有多少个有序点对 (x,y),满足 x 和 y 不连通。
注:这里的"有序点对"可以理解为 (x,y) 和 (y,x) 不相同 (x=y)。
输入格式
第一行包含两个整数 n 和 m。
接下来 m 行,每行包含两个整数 a 和 b,表示城镇 a 和 b 之间存在一条道路。
输出格式
输出共 n 行,每行输出一个整数。
第 i 行输出的整数表示把与节点 i 关联的所有边去掉以后(不去掉节点 i 本身),无向图有多少个有序点对 (x,y),满足 x 和 y 不连通。
输入输出样例
输入 #1复制
5 5
1 2
2 3
1 3
3 4
4 5
输出 #1复制
8
8
16
14
8
说明/提示
n≤105,m≤5×105。
实现代码:
cpp
#include<bits/stdc++.h>
using namespace std;
const int maxn=1000010;
inline int read()
{
int x=0,t=1;char ch=getchar();
while(ch>'9'||ch<'0'){if(ch=='-')t=-1;ch=getchar();}
while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
return x*t;
}
int n,m,head[maxn],num=0;
int dfn[maxn],low[maxn],size[maxn],tot=0;
long long ans[maxn];
bool cut[maxn];
struct node{
int v,nex;
}e[maxn];
void add(int u,int v)
{
e[++num].v=v;
e[num].nex=head[u];
head[u]=num;
}
void tarjan(int u)
{
dfn[u]=low[u]=++tot;
size[u]=1;
int flag=0,sum=0;
for(int i=head[u];i;i=e[i].nex)
{
int v=e[i].v;
if(!dfn[v])
{
tarjan(v);
size[u]+=size[v];
low[u]=min(low[u],low[v]);
if(low[v]>=dfn[u])
{
ans[u]+=(long long)size[v]*(n-size[v]);
sum+=size[v];
flag++;
if(u!=1||flag>1) cut[u]=true;
}
}
else low[u]=min(low[u],dfn[v]);
}
if(!cut[u]) ans[u]=2*(n-1);
else ans[u]+=(long long)(n-sum-1)*(sum+1)+(n-1);
}
int main()
{
n=read(),m=read();
for(int i=1;i<=m;i++)
{
int x,y;
x=read(),y=read();
add(x,y),add(y,x);
}
tarjan(1);
for(int i=1;i<=n;i++) printf("%lld\n",ans[i]);
return 0;
}