http://acm.sgu.ru/problem.php?contest=0&problem=219
题目大意:
如果指向某个点的边权全都为正数,那么这个点就是可点燃的,点燃操作把入弧权值-1,出弧权值都+1,
如果在某种点燃序列之后还可以再点燃一些点使得这个点还可以点燃,那么这个点在这种点燃序列之后存活
如果在任何点燃序列之后都还可以再点燃一些点使得这个点还可以点燃,那么这个点可存活
现在求所有点是否可存活
思路:
考虑不可存活的点:对于某个状态,对于不可存活的点,要想使得没有序列可以使它被点燃,那么有边指向它的点里一定有不可存活的点,且这条边权值为0,
如果有一个边权值都为0的环,那么在这条环上,由于权值都为0,所以这个环上的都是不可存活的点.
所以:先通过求边权值都为0的环确定一些初始点,由这些不可存活点出发到达的点,都在某个序列下因为这些不可存活点不能提供权值而不能存活
注意:
1. 有自环
2. tarjian找环需要注意更新dfn值的点在stack内,否则对于我写的形式
会有这种情况不正确
#include <cstdio> #include <stack> #include <cstring> using namespace std; const int maxn=1e3+3; const int maxm=5e4+4; int n,m; int first[maxn],head[maxn]; struct edge{ int t,nxt; }e[maxm],g[maxm]; int low[maxn],dp[maxn],depth; int alive[maxn]; bool in[maxn]; void addedge(int f,int t,int c,int ind){ if(c==0){ g[ind].nxt=head[f]; g[ind].t=t; head[f]=ind; } e[ind].nxt=first[f]; e[ind].t=t; first[f]=ind; } stack<int> st; void tarjian(int s){ low[s]=dp[s]=++depth; in[s]=true;st.push(s); for(int p=head[s];p!=-1;p=g[p].nxt){ int t=g[p].t; if(t==s){ alive[s]=0; } if(dp[t]==0){ tarjian(t); low[s]=min(low[s],low[t]); } else if(in[t]){//ATTHENTION: low[s]=min(low[s],dp[t]); } } bool single=true; if(low[s]==dp[s]){ while(st.top()!=s){ single=false; alive[st.top()]=0; in[st.top()]=false;st.pop(); } if(!single){ alive[st.top()]=0; } in[st.top()]=false;st.pop(); } } void dfs(int s){ for(int p=first[s];p!=-1;p=e[p].nxt){ int t=e[p].t; if(alive[t]==1){ alive[t]=0; dfs(t); } } } int main(){ scanf("%d%d",&n,&m); memset(first,-1,sizeof(first)); memset(head,-1,sizeof(head)); fill(alive,alive+n+1,1); for(int i=0;i<m;i++){ int f,t,c; scanf("%d%d%d",&f,&t,&c); addedge(f,t,c,i); } for(int i=1;i<=n;i++){ if(dp[i]==0){ tarjian(i); } } for(int i=1;i<=n;i++){ if(alive[i]==0){ dfs(i); } } for(int i=1;i<=n;i++){ printf("%d ",alive[i]); } return 0; }