P3119 [USACO15JAN]Grass Cownoisseur G
图论练习题
首先我们会发现可以缩下点,因为同一个连通分量里面就可以自由遍历所有的点,然后就可以得到一个拓扑图.....
注意是拓扑图不是树我也是服了自己
然后考虑在拓扑图上求这个答案,显然我们可以设计dp[i][0/1]表示点i是否已经走过反向边走到1的最长路径
然后显然这个东西要建反图才能处理每个点走到1的....
然后我们就这样建反图跑spfa就好了
显然,二维dp状态我们是可以用分层图的,所以可以再构造一个分层图,以更不错的常数解决此题
代码就用了这个
思维难度好小
code:
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
const int MAXN=4e5+7;
int n,m;
int home[MAXN],to[MAXN],nxt[MAXN],ccnt,frm[MAXN];
inline void ct(int x,int y) {
ccnt++;
nxt[ccnt]=home[x];
home[x]=ccnt;
to[ccnt]=y;
frm[ccnt]=x;
}
int first[MAXN],cccnt;
struct rec {
int to,nxt,vl;
} e[MAXN];
inline void ct2(int x,int y) {
cccnt++;
e[cccnt].nxt=first[x];
first[x]=cccnt;
e[cccnt].to=y;
}
int st[MAXN],tp,dfn[MAXN],dep,d[MAXN],low[MAXN],nw,num[MAXN],fl[MAXN];
inline void tarjan(int u) {
fl[u]=1;
dfn[u]=low[u]=++dep;
st[++tp]=u;
for(int i=home[u]; i; i=nxt[i]) {
int v=to[i];
if(!dfn[v]) {
tarjan(v);
low[u]=min(low[u],low[v]);
} else if(!fl[v])continue;
low[u]=min(dfn[v],low[u]);
}
if(low[u]==dfn[u]) {
++nw;
while(1) {
int v=st[tp];
fl[v]=0;
d[v]=nw;
num[nw]++;
--tp;
if(v==u)break;
}
}
return ;
}
int que[MAXN],head,tail,vis[MAXN],ans,dis[MAXN];
inline void spfa() {
int s=d[1];
head=tail=1;
que[tail++]=s;
while(head<tail) {
int u=que[head];
++head;
vis[u]=0;
// printf("%d!%d\n",u,dis[u]);
for(int i=first[u]; i; i=e[i].nxt) {
int v=e[i].to;
// printf("%d %d %d %d\n",v,dis[v],num[v],num[u]);
if(dis[v]<dis[u]+num[u]) {
dis[v]=dis[u]+num[u];
if(!vis[v]) {
vis[v]=1;
que[tail++]=v;
}
}
}
// puts("--------");
}
for(int i=1; i<=2*nw+1; ++i)ans=max(ans,dis[i]);
return ;
}
int main() {
scanf("%d%d",&n,&m);
for(int i=1,x,y; i<=m; ++i)
scanf("%d%d",&x,&y),ct(x,y);
for(int i=1; i<=n; ++i) {
if(!dfn[i])tarjan(i);
}
// for(int i=1; i<=n; ++i)printf("%d %d\n",i,d[i]);
for(int i=1; i<=nw; ++i)num[i+nw]=num[i];
for(int i=1; i<=m; ++i) {
int u=frm[i];
int v=to[i];
if(d[u]==d[v])continue;
// printf("%d %d %d %d\n",u,v,d[u],d[v]);
ct2(d[v],d[u]);
ct2(d[u],d[v]+nw);
ct2(d[v]+nw,d[u]+nw);
}
spfa();
printf("%d\n",dis[d[1]+nw]);
return 0;
}