P2746 [USACO5.3]校园网Network of Schools
翻译题面:
任务A:求缩点后的图中有多少个点入度为0。
任务B:求入度为0的点数与出度为0的点数的较大值。
注意避免连边时重复算出度入度,所以用set而非vector存图;只有一个点(缩点后)要特判。
写代码时注意定义For的话不能写成 For(j,0,g[i].size()-1),查了至少30min...

#include <bits/stdc++.h> #define ri register int #define For(i,l,r) for(ri i=l;i<=r;i++) using namespace std; const int M=105; vector<int> g[M]; stack<int> st; set<int> gg[M]; int n,dfn[M],low[M],inst[M],belong[M],in,cnt,indg[M]; inline void tarjan(int x){ low[x]=dfn[x]=++in;st.push(x);inst[x]=1; //For(i,0,g[x].size()-1){ for(int i=0;i<g[x].size();i++){ if(!dfn[g[x][i]]) tarjan(g[x][i]),low[x]=min(low[x],low[g[x][i]]); else if(inst[g[x][i]]) low[x]=min(low[x],dfn[g[x][i]]); } if(dfn[x]==low[x]){ cnt++; while(st.top()!=x){ belong[st.top()]=cnt; inst[st.top()]=0; st.pop(); } belong[st.top()]=cnt; inst[st.top()]=0; st.pop(); } } int main(){ cin>>n; For(i,1,n){int x;while(cin>>x && x)g[i].push_back(x);} For(i,1,n){if(!dfn[i])tarjan(i);} if(cnt==1) {cout<<1<<endl<<0<<endl;return 0;} For(i,1,n){ //For(j,0,g[i].size()-1){ for(int j=0;j<g[i].size();j++){ if(belong[g[i][j]]!=belong[i]&&gg[belong[i]].find(belong[g[i][j]])==gg[belong[i]].end()){ gg[belong[i]].insert(belong[g[i][j]]); indg[belong[g[i][j]]]++; } } } int ans1=0,ans2=0; For(i,1,cnt){ if(indg[i]==0) ans1++; if(gg[i].size()==0) ans2++; } cout<<ans1<<endl<<max(ans1,ans2)<<endl; return 0; }
P3119 [USACO15JAN]草鉴定Grass Cownoisseur
没有逆行就很简单,那么加上逆行之后我是想建一个反向图,新图和原图各跑一遍SPFA求最长边,然而然后就不知道了。
法一.%分%层%图%。
Tarjan缩点+强连通分量不多赘述。
"考虑一张图,将这个图复制一份,点的编号从1~N到(N+1)(N+1)(N+1)~(N+N)(N+N)(N+N)。然后在两层图中连边。对于原图上的每一条边,从原图的指向点到新图的起始点连一条边,边权与原边相同,代表逆向走一条边。逆向走了一条边,就不能再逆向走了,所以从上面的一层(新图)无法回到下面的一层。最后跑一遍SPFA,节点111所在的强连通分量编号,到节点111所在的强连通分量编号+NNN上的最长路,就是最后的答案"(https://www.luogu.org/blog/hsfzLZH1/solution-p3119)

#include<bits/stdc++.h> #define For(i,l,r) for(int i=l;i<=r;i++) using namespace std; const int M=1e5+5; const int inf=0x3f3f3f; vector<int> g[M]; vector<int> gg[M<<1]; int n,m,op,in,dfn[M],low[M],bcc[M],siz[M<<1],cnt,dist[M<<1]; bool inst[M<<1]; stack<int>st; queue<int>q; inline void tarjan(int x){ dfn[x]=low[x]=++in;inst[x]=1;st.push(x); for(int i=0;i<g[x].size();i++){ if(!dfn[g[x][i]]) tarjan(g[x][i]),low[x]=min(low[x],low[g[x][i]]); else if(inst[g[x][i]]) low[x]=min(low[x],dfn[g[x][i]]); } if(dfn[x]==low[x]){ cnt++; while(st.top()!=x){ bcc[st.top()]=cnt;inst[st.top()]=0;st.pop();siz[cnt]++; } bcc[st.top()]=cnt;inst[st.top()]=0;st.pop();siz[cnt]++; } } int main(){ scanf("%d%d",&n,&m); while(m--){int u,v; scanf("%d%d",&u,&v);g[u].push_back(v);} For(i,1,n) if(!dfn[i])tarjan(i); For(i,1,cnt)siz[cnt+i]=siz[i]; For(i,1,n){ for(int j=0;j<g[i].size();j++){ if(bcc[i]!=bcc[g[i][j]]){ gg[bcc[i]].push_back(bcc[g[i][j]]); gg[bcc[g[i][j]]].push_back(bcc[i]+cnt); gg[bcc[i]+cnt].push_back(bcc[g[i][j]]+cnt); } } } inst[bcc[1]]=1;q.push(bcc[1]); while(!q.empty()){ int x=q.front(); for(int i=0;i<gg[x].size();i++){ if(dist[gg[x][i]]<dist[x]+siz[x]){ dist[gg[x][i]]=dist[x]+siz[x]; if(!inst[gg[x][i]]) inst[gg[x][i]]=1,q.push(gg[x][i]); } } q.pop();inst[x]=0; } printf("%d\n",dist[bcc[1]+cnt]); return 0; }
法二.我“显而易见”(也想了3、5min吧,其实也是比较基础也比较好想的做法)的做法的完整版。