[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 }
View Code

 

posted @ 2021-06-10 14:58  PYWBKTDA  阅读(113)  评论(0编辑  收藏  举报