今天考的是APIO2009
然鹅我爆零了。。。
T1 MLE,T2没写,T3 RE+WA+TLE
T1 dp 写挂了,肝了2小时,以为自己写出来了,结果还是错了,而且还MLE了。所以洛谷测的20分,但是lemon上0分。
(洛谷还写的是宜参加模拟赛 我信你个鬼)
T2 第一问比较水 但是第二问把我卡住了 (据ZZX巨佬所言,这题我们大约去年10月的时候考过,但是我完全没印象??! 我看了题解 感觉还是有点懵)
T3 其实是缩点裸题 奈何我永远不记得缩点怎么写了(之前写过,但是忘了,也没太在意) 每次考场上遇到只能自己瞎写了QAQ
鉴于以上教训
我决定 先恶补一发tarjan缩点 (我觉得我也要恶补图论 毕竟我的图论太烂了) 然后立个flag 如果有时间 一定要写一篇 APIO2009 的题解
那么
进入正题吧!
先来一波定义~
有向图强连通分量:在有向图G中,如果两个顶点vi,vj间(vi>vj)有一条从vi到vj的有向路径,同时还有一条从vj到vi的有向路径,则称两个顶点强连通。如果有向图G的每两个顶点都强连通,称G是一个强连通图。有向图的极大强连通子图,称为强连通分量,简记为SCC。点击查看百度百科
流图:给定有向图G=(V,E),若存在r∈V,满足从r出发能够到达V中所有的点,则成G是一个“流图”,记为(G,r),其中r称为流图的源点。
时间戳:对于一个流图(G,r),我们对它从r开始进行深度优先遍历,每个节点只访问一次,每个节点被访问的时间顺序(即该节点是第几个被访问),称为该节点的时间戳。
对于每一个节点,我们用dfn[i]记录它的时间戳,用low[i]记录它的回溯值(即不经过流图搜索树上该节点父亲能返回的最小的时间戳)。
对于一个节点u,设其子树(流图搜索树中)为subtree(u),我们也可以这样定义它的low值:
对于有向图中满足以下条件的节点中最小的dfn值:
① 存在一条从subtree(u)出发的路径,以该节点为终点;
②遍历到u的子树时,该节点还在栈中。
(②是为了避免图中这种情况的出现,图中节点编号也是节点的时间戳,栈中保留的节点应该是u的祖先节点及已经访问过且存在一条边到u的祖先节点的节点)
这样我们就可以求low值啦:
枚举u的每一条出边,设该边的终点为v
①若v已经访问过并v还在栈中 则直接low[u]=min(low[u],dfn[v])。
②若v还没访问过 就将v加入栈中 并将low[u]和low[v]取min。
最后 判断一下 dfn[u]是否等于low[u],
若相等 就直接弹出栈中的元素(弹到是u就停止),则这些元素构成一个强连通分量(有可能只有一个元素)。
相等说明 u子树内的顶点不可能与栈内u以前的节点构成一个环。
(自己手动模拟一下就明白啦~)
1 void dfs(int po,int fa) 2 { 3 if(!dfn[po]) dfn[po]=low[po]=++tim; 4 sta[++tl]=po,in[po]=1;//入栈 5 vis[po]=1; 6 for(int i=hd[po];i;i=e[i].n) 7 { 8 int v=e[i].v; 9 if(vis[v]&&in[v]) low[po]=min(low[po],dfn[v]);//如果能回到比它时间戳更早的节点 10 else if(!vis[v]) //如果该节点还未访问过 11 { 12 dfs(v,po); 13 low[po]=min(low[po],low[v]); 14 } 15 } 16 if(dfn[po]==low[po])//回溯前将栈内元素弹出 17 { 18 bool bb=0; 19 cnt++; 20 while(!bb){ 21 if(sta[tl]==po) bb=1; 22 cir[sta[tl]]=cnt; 23 cw[cnt]+=w[sta[tl]];//此处 记录一个强连通分量内的节点的权值之和(后面板子题以及APIO2009T3要用到) 24 in[sta[tl]]=0; 25 sta[tl--]=0; 26 } 27 } 28 }
以上就是求tarjan缩点的核心代码~
练习
1.一道板子题
参考代码如下:
1 #include2 #include 3 #include 4 #define gc cr==cl&&(cr=(cl=bu)+fread(bu,1,100000,stdin),cl==cr)?EOF:*cl++ 5 #define gs (ch<'0'||ch>'9') 6 #define r(x) x=read() 7 using namespace std; 8 const int N=1e4+111,M=1e5+111; 9 int n,m,tot,cnt,tim,ans,tl; 10 int hc[N],hd[N],dfn[N],low[N],cir[N],cw[N],w[N],sta[N],f[N],du[N]; 11 bool vis[N],in[N]; 12 char bu[100011],*cr,*cl; 13 int read() 14 { 15 int x=0;char ch=gc; 16 while( gs) ch=gc; 17 while(!gs) x=x*10+ch-48,ch=gc; 18 return x; 19 } 20 struct edge{ 21 int n,v; 22 }e[M]; 23 void add(int u,int v) 24 { 25 e[++tot]=(edge){hd[u],v}; 26 hd[u]=tot; 27 } 28 struct Edge{ 29 int n,v; 30 }E[M]; 31 void addcir(int u,int v) 32 { 33 du[v]++; 34 E[++tot]=(Edge){hc[u],v}; 35 hc[u]=tot; 36 } 37 void dfs(int po) 38 { 39 if(!dfn[po]) dfn[po]=low[po]=++tim; 40 sta[++tl]=po,in[po]=1; 41 vis[po]=1; 42 for(int i=hd[po];i;i=e[i].n) 43 { 44 int v=e[i].v; 45 if(vis[v]&&in[v]) low[po]=min(low[po],dfn[v]); 46 else if(!vis[v]) 47 { 48 dfs(v); 49 low[po]=min(low[po],low[v]); 50 } 51 } 52 if(dfn[po]==low[po]) 53 { 54 bool bb=0; 55 cnt++; 56 while(!bb){ 57 if(sta[tl]==po) bb=1; 58 cir[sta[tl]]=cnt; 59 cw[cnt]+=w[sta[tl]]; 60 in[sta[tl]]=0; 61 sta[tl--]=0; 62 } 63 } 64 } 65 void dfs1(int po,int sum) 66 { 67 if(sum<=f[po]) return ; 68 f[po]=sum; 69 vis[po]=1; 70 ans=max(ans,sum); 71 for(int i=hc[po];i;i=E[i].n) 72 { 73 int v=E[i].v; 74 dfs1(v,sum+cw[v]); 75 } 76 } 77 int main() 78 { 79 r(n),r(m); 80 for(int i=1;i<=n;i++) r(w[i]); 81 for(int i=1;i<=m;i++) 82 { 83 int u,v; 84 r(u),r(v); 85 add(u,v); 86 } 87 for(int i=1;i<=n;i++) if(!dfn[i]) dfs(i); 88 tot=0; 89 for(int i=1;i<=n;i++) 90 for(int j=hd[i];j;j=e[j].n) 91 if(cir[i]!=cir[e[j].v]) 92 addcir(cir[i],cir[e[j].v]); 93 memset(vis,0,sizeof(vis));memset(f,-1,sizeof(f)); 94 for(int i=1;i<=cnt;i++) if(du[i]==0&&vis[i]==0) dfs1(i,cw[i]); 95 printf("%d",ans); 96 return 0; 97 }
缩点了以后整个图就是一个DAG(有向无环图)
这道题主要是缩点然后dfs一遍就可以了,也可以用DP做(不过我没写DP)。
这道题真的和今天T3基本一样!改改细节就可以了!QAQ!
2.当然是今天T3
大部分和上面相同 但要是dfs的话会T掉一个点
于是乎 借鉴题解(考虑到固定1个点出发) 写了spfa(呜呜呜spfa写错了一个地方调了半个小时)
参考代码如下:
1 #include2 #include 3 #include 4 #define gc cr==cl&&(cr=(cl=bu)+fread(bu,1,100000,stdin),cl==cr)?EOF:*cl++ 5 #define gs (ch<'0'||ch>'9') 6 #define r(x) x=read() 7 using namespace std; 8 const int N=5e5+111,M=5e5+111; 9 int n,m,tot,cnt,tim,ans,tl,p,S; 10 int que[N],hc[N],hd[N],dfn[N],low[N],cir[N],cw[N],w[N],sta[N],dis[N]; 11 bool in[N],book[N],book1[N]; 12 char bu[100011],*cr,*cl; 13 int read() 14 { 15 int x=0;char ch=gc; 16 while( gs) ch=gc; 17 while(!gs) x=x*10+ch-48,ch=gc; 18 return x; 19 } 20 struct edge{ 21 int n,v; 22 }e[M]; 23 void add(int u,int v) 24 { 25 e[++tot]=(edge){hd[u],v}; 26 hd[u]=tot; 27 } 28 struct Edge{ 29 int n,v,w; 30 }E[M]; 31 void addcir(int u,int v,int w) 32 { 33 E[++tot]=(Edge){hc[u],v,w}; 34 hc[u]=tot; 35 } 36 void dfs(int po) 37 { 38 dfn[po]=low[po]=++tim; 39 sta[++tl]=po,in[po]=1; 40 for(int i=hd[po];i;i=e[i].n) 41 { 42 int v=e[i].v; 43 if(dfn[v]&&in[v]) low[po]=min(low[po],dfn[v]); 44 else if(!dfn[v]) 45 { 46 dfs(v); 47 low[po]=min(low[po],low[v]); 48 } 49 } 50 if(dfn[po]==low[po]) 51 { 52 bool bb=0; 53 cnt++; 54 while(!bb){ 55 if(sta[tl]==po) bb=1; 56 if(book[sta[tl]]) book1[cnt]=1; 57 cir[sta[tl]]=cnt; 58 cw[cnt]-=w[sta[tl]]; 59 in[sta[tl]]=0; 60 sta[tl--]=0; 61 } 62 } 63 } 64 void spfa() 65 { 66 S=cir[S]; 67 memset(book,0,sizeof(book)); 68 int he=1,ta=2; 69 que[he]=S; 70 while(he<ta) 71 { 72 int tt=que[he]; 73 for(int i=hc[tt];i;i=E[i].n) 74 { 75 int v=E[i].v; 76 if(dis[tt]+E[i].w<dis[v]) 77 { 78 dis[v]=dis[tt]+E[i].w; 79 if(!book[v]) 80 { 81 que[ta++]=v; 82 book[v]=1; 83 } 84 } 85 } 86 book[tt]=0; 87 he++; 88 } 89 for(int i=1;i<=cnt;i++) if(book1[i])ans=min(ans,dis[i]); 90 ans+=cw[S]; 91 ans*=-1; 92 } 93 int main() 94 { 95 freopen("atm.in","r",stdin); 96 freopen("atm.out","w",stdout); 97 r(n),r(m); 98 for(int i=1;i<=m;i++) 99 { 100 int u,v; 101 r(u),r(v); 102 add(u,v); 103 } 104 for(int i=1;i<=n;i++) r(w[i]); 105 r(S),r(p); 106 for(int i=1;i<=p;i++){int x;r(x);book[x]=1;} 107 dfs(S); 108 tot=0; 109 for(int i=1;i<=n;i++) 110 if(dfn[i]) for(int j=hd[i];j;j=e[j].n) 111 if(cir[i]!=cir[e[j].v]) 112 addcir(cir[i],cir[e[j].v],cw[cir[e[j].v]]); 113 spfa(); 114 printf("%d",ans); 115 return 0; 116 }
那么,就先结束啦~
感谢阅读~