[cf1616G]Just Add an Edge

记边集为$E$,新建点$0$向$[1,n]$连边$,n+1$从$[1,n]$连边,以此确定起点和终点

若初始$\forall 0\le i\le n,(i,i+1)\in E$,显然答案即${n\choose 2}$,不妨特判此类情况

此时考虑加入$(x,y)$后能否合法,不难证明路径必然为以下形式
$$
0\rightarrow (y-1)\rightsquigarrow x\rightarrow y\rightsquigarrow (x+1)\rightarrow (n+1)
$$
说明:

1.$x\rightarrow y$使用新建的边$(x,y)$

2.$0\rightarrow (y-1)$和$(x+1)\rightarrow (n+1)$要求$\forall i\in [0,y-2]\cup [x+1,n],(i,i+1)\in E$

3.$(y-1)\rightsquigarrow x$和$y\rightsquigarrow (x+1)$要求经过的点无交且并为$[y-1,x+1]$

根据初始特判的条件,任选$0\le z\le n$使得$(z,z+1)\not\in E$(必然存在)

若第2个条件成立,则$z\not\in [0,y-2]\cup [x+1,n]$,进而即有$y-1\le z\le x$

不难证明,(第3点中)两条链不断向前拓展,总会经过状态$(z,z+1)$或$(z+1,z)$

换言之,从这两个状态出发,向两侧分别去搜索所有形如$(k,k\pm 1)$的状态即可

时间复杂度为$o(n+m)$,可以通过

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 #define N 150005
 4 #define ll long long
 5 vector<int>v[N],rev_v[N];
 6 int t,n,m,x,y,fa[N],X[4],Y[4],vis[N][2];
 7 ll ans;
 8 int find(int k){
 9     if (k==fa[k])return k;
10     return fa[k]=find(fa[k]);
11 }
12 void merge(int x,int y){
13     x=find(x),y=find(y);
14     if (x!=y)fa[x]=y;
15 }
16 void add(int x,int y){
17     if (y==x+1)merge(x,y);
18     v[x].push_back(y);
19     rev_v[y].push_back(x);
20 }
21 void dfs1(int k,int p,int P){
22     if (vis[k][p+1>>1]&P)return;
23     vis[k][p+1>>1]|=P;
24     if (p>0){
25         for(int i=0;i<v[k].size();i++)
26             if ((v[k][i]>k+1)&&(fa[k+1]==fa[v[k][i]-1]))dfs1(v[k][i],-1,P);
27     }
28     else{
29         for(int i=0;i<v[k-1].size();i++)
30             if ((v[k-1][i]>k)&&(fa[k]==fa[v[k-1][i]-1]))dfs1(v[k-1][i]-1,1,P);
31     }
32 }
33 void dfs2(int k,int p,int P){
34     if (vis[k][p+1>>1]&P)return;
35     vis[k][p+1>>1]|=P;
36     if (p>0){
37         for(int i=0;i<rev_v[k+1].size();i++)
38             if ((rev_v[k+1][i]<k)&&(fa[k]==fa[rev_v[k+1][i]+1]))dfs2(rev_v[k+1][i]+1,-1,P);
39     }
40     else{
41         for(int i=0;i<rev_v[k].size();i++)
42             if ((rev_v[k][i]<k-1)&&(fa[k-1]==fa[rev_v[k][i]+1]))dfs2(rev_v[k][i],1,P);
43     }
44 }
45 int main(){
46     scanf("%d",&t);
47     while (t--){
48         scanf("%d%d",&n,&m);
49         for(int i=0;i<=n+1;i++){
50             fa[i]=i;
51             v[i].clear(),rev_v[i].clear();
52         }
53         for(int i=1;i<=n;i++)add(0,i),add(i,n+1);
54         for(int i=1;i<=m;i++){
55             scanf("%d%d",&x,&y);
56             add(x,y);
57         }
58         for(int i=0;i<=n+1;i++)fa[i]=find(i);
59         if (fa[0]>n){
60             printf("%lld\n",(ll)n*(n-1)/2);
61             continue;
62         }
63         memset(vis,0,sizeof(vis));
64         dfs1(fa[0],1,1),vis[fa[0]][1]=0,dfs2(fa[0],1,1);
65         dfs1(fa[0]+1,-1,2),vis[fa[0]+1][0]=0,dfs2(fa[0]+1,-1,2);
66         for(int p=1;p<4;p++)X[p]=Y[p]=0;
67         for(int i=1;i<=fa[0]+1;i++)
68             for(int p=1;p<4;p++)
69                 if ((p&vis[i-1][1])==p)X[p]++;
70         for(int i=fa[0];i<=n;i++)
71             if (fa[i+1]>n){
72                 for(int p=1;p<4;p++)
73                     if ((p&vis[i][1])==p)Y[p]++;
74             }
75         ans=(ll)X[1]*Y[1]+(ll)X[2]*Y[2]-(ll)X[3]*Y[3];
76         if (fa[fa[0]+1]>n)ans--;
77         printf("%lld\n",ans);
78     }
79     return 0;
80 }
View Code

 

posted @ 2022-02-05 20:36  PYWBKTDA  阅读(62)  评论(0编辑  收藏  举报