BZOJ3415: Poi2013 Price List

来源:互联网 发布:泰国4g网络制式 频段 编辑:程序博客网 时间:2024/04/19 03:50
题目大意:给一个无向图,边权均为a,然后将原来图中满足最短路等于2a所有的点对(x,y)之间再加一条长度为b的无向边,问操作之后点K到所有点的最短路是多少

首先我们考虑最短路的几种情况:1.就是按照边权全是a那么走
2.(当b<2a时)可以将原来最短路中每两条边合成一个b走下来
3.(当b<a时)这时有一种奇特的走法,比如当原来最短路长度是奇数时沿着原来的走就必然要走一个a,假如a很大就不划算了,所以可以在原图上找到一种边数较多的方法,但是恰好相邻两点间都是b,这样路程更短

前两种可以直接BFS出来,关键是第三种
首先可以想到一个比较暴力的方法,还是BFS,然后对于每个点,先遍历他的所有相邻结点,再遍历这些点的相邻结点,然后把它们推进队列,这样时间复杂度可以达到O(M^2)
然后我们可以想到一个小优化
当我们取出队头一个元素后,先遍历和他相邻的节点,我们称之为一次遍历,然后再遍历第二波相邻结点,我们称之为二次遍历
我们可以发现,当一次遍历扩展到一个节点x,并由他再二次遍历扩展到一个新的节点y时,边(x,y)再二次遍历中就再也没有用了,因为y这个点已经进入了队列而x没有,也就是说,x还有机会成为一次遍历产生的点,这时再由他二次遍历时,原来已经进入队列的就不需要在进入队列了,所以这条边没用了可以删掉,删掉可以用DCEL(双向边表)
然而这不仅仅是个小优化

POI官网上给出了时间复杂度证明:
首先,时间复杂度约等于遍历的边的数量
所以我们只需要考虑那些遍历了却没被删掉的边的数量
对于每一个节点x,由他开始进行一次遍历再二次遍历中,没被删掉的边只有一种,就是在二次遍历中遍历到了一个与x距离为1的点,也就是一个三元环
所以对于这个节点x,假设和他距离为1的点有k个,娜美这次最多有O(k^2)条边遍历过但没有被删掉
又因为总边数一共只有m条,所以总时间复杂度为
出题人怎么想到的......

#include<iostream>#include<cstdio>#include<cstring>#include<algorithm>#define N 200010#define ZZ(i,x) for(i=Z.pre[x];i;i=Z.nxt[i])#define FF(i,x) for(i=F.pre[x];i;i=F.nxt[i])using namespace std;struct ljb{int to[N],nxt[N],pre[N],bef[N],cnt;void ae(int ff,int tt){cnt++;to[cnt]=tt;nxt[cnt]=pre[ff];bef[pre[ff]]=cnt;pre[ff]=cnt;}void del(int x,int i){if(i==pre[x]) pre[x]=nxt[i];nxt[bef[i]]=nxt[i];bef[nxt[i]]=bef[i];}}Z,F;int q[N],d[N],ans[N];bool vis[N];int main(){int n,m,s,a,b;scanf("%d%d%d%d%d",&n,&m,&s,&a,&b);int i,j,k,l,x,y;for(i=1;i<=m;i++){scanf("%d%d",&x,&y);Z.ae(x,y);Z.ae(y,x);F.ae(x,y);F.ae(y,x);}int h=1,t=1;q[1]=s;while(h<=t){x=q[h];h++;ZZ(i,x){j=Z.to[i];if(!d[j]&&j!=s){d[j]=d[x]+1;t++;q[t]=j;}}}for(i=1;i<=n;i++)ans[i]=min(d[i]*a,(d[i]/2)*b+(d[i]%2)*a);memset(d,0,sizeof(d));h=1;t=1;q[1]=s;while(h<=t){x=q[h];h++;ZZ(i,x){j=Z.to[i];vis[j]=true;}ZZ(i,x){j=Z.to[i];FF(k,j){l=F.to[k];if(d[l]||l==s||vis[l]) continue;d[l]=d[x]+1;t++;q[t]=l;F.del(j,k);}}ZZ(i,x){j=Z.to[i];vis[j]=false;}}for(i=1;i<=n;i++)if(d[i])ans[i]=min(ans[i],d[i]*b);for(i=1;i<=n;i++)printf("%d\n",ans[i]);}

1 0