[luogu7417]Minimizing Edges P
令$e_{G}(a)$和$o_{G}(a)$分别表示在图$G$中从1到$a$的长度为奇数/偶数的最短路(若该类最短路不存在则为$\infty$),不难得到有以下结论——$f_{G}(a,b)=\begin{cases}[b\ge e_{G}(a)]&(b\equiv 0(mod\ 2))\\ [b\ge o_{G}(a)]&(b\equiv 1(mod\ 2))\end{cases}$
根据这个结论,即要求$\forall a,e_{G}(a)=e_{G'}(a)$且$o_{G}(a)=o_{G'}(a)$
先对原图$G$求出所有$e_{G}(a)$和$o_{G}(a)$,将$a$拆为$a_{0}$和$a_{1}$,并对边$(a,b)$连$(a_{0},b_{1})$和$(a_{1},b_{0})$,最后从$1_{0}$出发bfs即可,时间复杂度为$o(n)$
记$G'$的边集为$E$,那么$\forall a,e_{G}(a)=e_{G'}(a)$且$o_{G}(a)=o_{G'}(a)$当且仅当满足以下两个条件:
1.$(a,b)\in E,|e_{G}(a)-o_{G}(b)|=1$且$|e_{G}(b)-o_{G}(a)|=1$(特别的,定义$|\infty-\infty|=1$)
2.$\forall e_{G}(a)\ne 0,\infty,\exists (a,b)\in E,e_{G}(a)=o_{G}(b)+1$且$\forall o_{G}(a)\ne \infty,\exists (a,b)\in E,o_{G}(a)=e_{G}(b)+1$
(关于这个结论,必要性显然,充分性拆点后对距离从小到大归纳即可)
若存在$a$满足$e_{G}(a)=\infty$,那么根据第1个条件,与其相连的点$o_{G}(b)=\infty$,以此类推,所有点(原图连通)$b$都满足$e_{G}(b)=\infty$或$o_{G}(b)=\infty$($o_{G}(a)=\infty$同理)
此时,对于第2个条件,除1以外(1没有限制)每一个点仅有1维有限制,只需要连向一个可以使其该维满足第2个条件的点,显然这样的点必然存在,最终的边数即为$n-1$
(也即原图没有奇环,不能调整奇偶性,构造方案即取以1为根的最短路径树)
考虑这种情况后,即$\forall a,e_{G}(a),o_{G}(a)\ne \infty$,将其作为点$(\min(e_{G}(a),o_{G}(a)),\max(e_{G}(a),o_{G}(a)))$,并将所有点$(x,y)$按照$x+y$从小到大排序、$x+y$相同时$x$从小到大排序
现在,我们从前往后,依次考虑当前点$(x,y)$,去连边满足其第2个条件
如果之前存在点$(x-1,y+1)$“未完全合法”,显然从中任选一个连边即可,连边后$(x,y)$也成为一个“未完全合法”的点(还需要与$(x\pm 1,y-1)$连边),暂不处理
否则,如果之前存在点$(x-1,y-1)$,直接连边即可,即满足条件
否则,再找到$(x-1,y+1)$连边(若$x=0$时不需要连,否则必然存在),并作为“未完全合法”的点
另外,若$y=x+1$且$(x,y)$作为“未完全合法”的点,注意到$(x+1,y-1)$实际上是$(x,y)$自己,因此若之间存在点$(x,y)$“未完全合法”,将这两点连边即可(并取消两点“未完全合法”的标记)
最终,对于剩下的“未完全合法”的点$(x,y)$,找到$(x\pm 1,y-1)$连边即可,由于必然存在,即边数加上“未完全合法”的点数量即可
(当然这个数量也可以在修改过程中顺便加上)
由此,用map维护$(x,y)$上“未完全合法”的点数量即可支持此过程,时间复杂度为$o(n\log n)$
(关于这一做法的正确性,即是一个贪心,比较显然)
1 #include<bits/stdc++.h> 2 using namespace std; 3 #define N 200005 4 struct Edge{ 5 int nex,to; 6 }edge[N<<2]; 7 queue<int>q; 8 vector<pair<int,int> >v; 9 map<int,int>mat_vis[N],mat[N]; 10 int E,t,n,m,x,y,ans,head[N],vis[N],d[N]; 11 void add(int x,int y){ 12 edge[E].nex=head[x]; 13 edge[E].to=y; 14 head[x]=E++; 15 } 16 void bfs(){ 17 d[1]=0; 18 q.push(1); 19 vis[1]=1; 20 while (!q.empty()){ 21 int k=q.front(); 22 q.pop(); 23 for(int i=head[k];i!=-1;i=edge[i].nex) 24 if (!vis[edge[i].to]){ 25 d[edge[i].to]=d[k]+1; 26 q.push(edge[i].to); 27 vis[edge[i].to]=1; 28 } 29 } 30 } 31 int main(){ 32 scanf("%d",&t); 33 while (t--){ 34 scanf("%d%d",&n,&m); 35 E=ans=0; 36 for(int i=0;i<=(n<<1);i++){ 37 head[i]=d[i]=-1; 38 vis[i]=0; 39 mat_vis[i].clear(),mat[i].clear(); 40 } 41 for(int i=1;i<=m;i++){ 42 scanf("%d%d",&x,&y); 43 add(x,y+n); 44 add(y+n,x); 45 add(x+n,y); 46 add(y,x+n); 47 } 48 bfs(); 49 if (d[n+1]<0){ 50 printf("%d\n",n-1); 51 continue; 52 } 53 v.clear(); 54 for(int i=1;i<=n;i++)v.push_back(make_pair(min(d[i],d[i+n]),max(d[i],d[i+n]))); 55 sort(v.begin(),v.end()); 56 for(int i=0;i<n;i++)mat_vis[v[i].first][v[i].second]=1; 57 for(int i=0;i<n;i++){ 58 x=v[i].first,y=v[i].second; 59 if ((x)&&(mat[x-1][y+1])){ 60 mat[x-1][y+1]--; 61 mat[x][y]++; 62 ans++; 63 } 64 else{ 65 if ((x)&&(mat_vis[x-1][y-1]))ans++; 66 else{ 67 mat[x][y]++; 68 ans+=1+(x>0); 69 } 70 } 71 if ((y==x+1)&&(mat[x][y]>=2)){ 72 mat[x][y]-=2; 73 ans--; 74 } 75 } 76 printf("%d\n",ans); 77 } 78 }