并不对劲的bzoj3832: [Poi2014]Rally
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了并不对劲的bzoj3832: [Poi2014]Rally相关的知识,希望对你有一定的参考价值。
这题的原理看上去很神奇。
称拓扑图中入度为0的点为“起点”,出度为0的点为“终点”。
因为“起点”和“终点”可能有很多个,算起来会很麻烦,所以新建“超级起点”S,向所有点连边,“超级终点”T,所有点向它连边。这样原图中的最长路就是新图中的最长路-2。
dis[a->b]表示a到b的距离。
对于一个拓扑图而言,它的一个割集中肯定有一条边在最长路上。对于每条边,可以将dis[S->该边起点]和dis[该边终点->T]算出,那么该边所在路径中的最长的一个就是dis[S->该边起点]+dis[该边终点->T]+1。将该边边权设为它。这样一来,最长路就是该图随便一个割集的边权最大值。
删掉一个点,就可以看成删掉这个点的所有出边。但是这样就有些问题了:所有能走到这条边的边和这条边能走到的所有边都会因为这条边删掉而受影响,要是把所有的影响都算出来,想必是不行的。这时就有一个看似可行的方法了:在删掉这个点的所有出边的图中求一个只包含与这些边不互达的边的割集,取这个割集中的边权最大值。会发现那个割集就是原图中由这个点的所有出边和一些与它不互达的边组成的一个割集。
会不会出现不互达的边不够用的情况?先把拓扑图按最长路分好层(浅蓝色的线)。
这样的话,就不会存在右->左的边。
也就是说,图中所有红边和所有蓝边都是不互达的。而删掉所有红边和所有蓝边后,就不能从左边走到右边了。这样就形成了一个割集。答案就是所有蓝边的边权最大值。
如果这个删掉的点后图变成两部分怎么办?想必是不可能的,因为对于每一个点,都有 S->该点 和 该点->T 的边,所以只要不删S、T就不会将图分成几部分。肯定会有上图中蓝边那样的边存在的。
那么问题就变成了对于每个点,求出所有与它不互达的边权的最大值,再对于所有点取最小值就行了!
好吧,这并没有把问题简化多少,至少变得相对可做了。
那么可以考虑递推。按照拓扑序枚举要删掉的点,每次将当前点变成下一个时,新的那个点的所有入边要删去,上一个点的所有出边要加入。
这是为什么呢?因为上一个状态是删掉上一个点的图,计入答案的是割集中不包括与上一个点可达的边的边权。加入上一个点的所有出边后,就刚好凑成了一个割集。此时计入答案的是原图的一个割集。但是我们要求的是删除新的点的割集,所以就要删除所有连向新的点的边。这时不会有其它与能走到该点的边。这是因为能走到该点的边的起点、终点的拓扑序都小于该点(终点是该点的边刚刚已经被删去了,不考虑)。也就是说,所有这些边的终点之前已经被考虑过了,它们的入边已经全部被删除了。而该点能到达的边的起点的拓扑序在该点之后,还没有被加入,所有不用考虑。
每次删除新的点的所有出边,会多删除吗?想必是不会的。新的点的所有入边的起点在拓扑序中都比该点靠前,所以所有入边之前都被加入了。
用什么数据结构维护呢?看上去需要支持取最大值、插入、删除,堆似乎不错。
刚刚证明了这种方法是可行的,但至于具体是怎么想出来的,我能说是去看某个大佬的博客吗?
#include<algorithm> #include<cassert> #include<cmath> #include<cstdio> #include<cstdlib> #include<cstring> #include<iomanip> #include<iostream> #include<map> #include<queue> #include<stack> #include<vector> #define rep(i,x,y) for(register int i=(x);i<=(y);++i) #define dwn(i,x,y) for(register int i=(x);i>=(y);--i) #define re register #define maxn 500010 #define maxm 2000010 using namespace std; inline int read() { int x=0,f=1; char ch=getchar(); while(isdigit(ch)==0 && ch!=\'-\')ch=getchar(); if(ch==\'-\')f=-1,ch=getchar(); while(isdigit(ch))x=(x<<3)+(x<<1)+ch-\'0\',ch=getchar(); return x*f; } inline void write(int x) { int f=0;char ch[20]; if(!x){putchar(\'0\'),putchar(\' \');return;} if(x<0){putchar(\'-\');x=-x;} while(x)ch[++f]=x%10+\'0\',x/=10; while(f)putchar(ch[f--]); putchar(\' \'); } int fir[2][maxn],nxt[2][maxm],v[2][maxm],id[2][maxm],cnt[2];//0:roads; 1:back roads; int S,T,n,m,mx,mxnd; int q[maxn],hd,tl,dis[2][maxn],in[maxn]; void ade(int u1,int v1,int id1,int f){v[f][cnt[f]]=v1,id[f][cnt[f]]=id1,nxt[f][cnt[f]]=fir[f][u1],fir[f][u1]=cnt[f]++;} priority_queue<int>yes,no; void inq(int x){yes.push(x);while(!no.empty()&&yes.top()==no.top())yes.pop(),no.pop();} void deq(int x){no.push(x);while(!no.empty()&&yes.top()==no.top())yes.pop(),no.pop();} int top(){while(!no.empty()&&yes.top()==no.top())yes.pop(),no.pop();return yes.top();} int tmp[maxn],cntt;/* void print() { cout<<"heap:"<<endl; while(!no.empty()&&yes.top()==no.top())yes.pop(),no.pop(); while(!yes.empty())cout<<yes.top()<<" ",tmp[++cntt]=yes.top(),yes.pop(); while(cntt)yes.push(tmp[cntt--]);cout<<endl; }*/ int main() { memset(fir,-1,sizeof(fir)); n=read(),m=read(); rep(i,1,m){int x=read(),y=read();ade(x,y,i,0),ade(y,x,i,1);in[y]++;}S=0,T=n+1; rep(i,1,n)ade(S,i,i+m,0),ade(i,S,i+m,1),ade(i,T,i+m,0),ade(T,i,i+m,1),in[i]++,in[T]++; q[++tl]=S; while(hd<tl) { int u=q[++hd]; for(int k=fir[0][u];k!=-1;k=nxt[0][k]) { int vv=v[0][k];in[vv]--; if(in[vv]==0){q[++tl]=vv;} } } rep(i,1,tl) { int u=q[i]; for(int k=fir[0][u];k!=-1;k=nxt[0][k]) { int vv=v[0][k]; dis[0][vv]=max(dis[0][u]+1,dis[0][vv]); } } dwn(i,tl,1) { int u=q[i]; for(int k=fir[0][u];k!=-1;k=nxt[0][k]) { int vv=v[0][k]; dis[1][u]=max(dis[1][vv]+1,dis[1][u]); } } mx=2147483647; rep(i,2,tl-1) { int u=q[i],pu=q[i-1]; for(int k=fir[0][pu];k!=-1;k=nxt[0][k]) { int vv=v[0][k]; inq(dis[0][pu]+dis[1][vv]+1); } for(int k=fir[1][u];k!=-1;k=nxt[1][k]) { int vv=v[1][k]; deq(dis[0][vv]+dis[1][u]+1); } if(mx>top())mx=top(),mxnd=u; } write(mxnd),write(mx-2); return 0; } /* 6 5 1 3 1 4 3 6 3 4 4 5 */
以上是关于并不对劲的bzoj3832: [Poi2014]Rally的主要内容,如果未能解决你的问题,请参考以下文章