• 【LGR114C】地地铁铁


    题意:

    给定 n n n 个点 m m m 条边的无向连通图,每条边标有 0 0 0 1 1 1,统计无序点对 ( x , y ) (x,y) (x,y) 的数量,满足 x , y x,y x,y 之间存在一条简单路径(点不重),使得这条简单路径既经过 0 0 0 边也经过 1 1 1 边。

    n ≤ 4 × 1 0 5 n\leq 4\times 10^5 n4×105 m ≤ 1 0 6 m\leq 10^6 m106

    题解:

    首先变为统计 ( x , y ) (x,y) (x,y) 之间任意简单路径都只经过 0 0 0 边或只经过 1 1 1 边的 ( x , y ) (x,y) (x,y) 的对数。

    分类讨论,考虑 ( x , y ) (x,y) (x,y) 之间所有简单路径都只经过 0 0 0 边的点对数量。

    借着这个机会,系统总结一下几个点双的基本性质。接下来我们称 “非平凡点双” 为点数 ≥ 3 \geq 3 3 的点双。

    • 引理 1:对于非平凡点双 S S S 和任意 S S S 内两个不同的点 a , b a,b a,b,存在两条 a , b a,b a,b 间的点不交(除 a , b a,b a,b 外,以后将不再说明此事)的简单路径 A , B A,B A,B

      注意点双的定义是:删掉任意一个点后原图仍连通。所以这个引理不是废话(

      证明:考虑对 a , b a,b a,b 间的最短路 d ( a , b ) d(a,b) d(a,b) 归纳证明。

      d ( a , b ) = 1 d(a,b)=1 d(a,b)=1 即存在边 ( a , b ) (a,b) (a,b) 时。若不存在另外一条 a , b a,b a,b 间的路径,那么删掉边 ( a , b ) (a,b) (a,b) a , b a,b a,b 将不连通,不妨设此时 a a a 所在的连通块大小 ≥ 2 \geq 2 2,那么在原图中删掉 a a a 将会导致原图不连通,矛盾。

      d ( a , b ) > 1 d(a,b)>1 d(a,b)>1 时。考虑 a , b a,b a,b 间的任意一条最短路 a − ⋯ − c − b a-\cdots-c-b acb。根据假设,存在两条 a , c a,c a,c 间的点不交简单路径 A , B A,B A,B。根据定义,删去 c c c a , b a,b a,b 仍然连通,此时取一条 a , b a,b a,b 间的简单路径 P P P,那么 P P P 不经过 c c c。如图:

      在这里插入图片描述

      不妨设 P P P A , B A,B A,B 第一次相交在 B B B 上的点 d d d,那么 b → P d → B a b\xrightarrow{P}d\xrightarrow{B}a bP dB a b → c → A a b\rightarrow c\xrightarrow{A}a bcA a 即为两条点不交的所求路径。

    • 推论 2:对于点数 ≥ 3 \geq 3 3 的图 G G G G G G 是点双连通分量当且仅当对于任意 G G G 中的两个不同的点 a , b a,b a,b,存在两条 a , b a,b a,b 间的点不交的简单路径 A , B A,B A,B

    • 引理 3:对于点双 S S S 和任意 S S S 内三个不同的点 a , b , c a,b,c a,b,c(这蕴含 S S S 是非平凡的),存在一条简单路径依次经过 a , b , c a,b,c a,b,c

      证明:如图,任取两条 a , b a,b a,b 间的点不交的简单路径 A , B A,B A,B,再任取一条 b , c b,c b,c 间的且不经过 a a a 的路径 P P P,根据点双的定义,这三条路径都是可以取得到的。

      在这里插入图片描述

      不妨设 P P P A , B A,B A,B 最后一次相交在 B B B 上的点 d d d(那么 d ≠ a d\neq a d=a),于是 a → A b → B d → P c a\xrightarrow{A}b\xrightarrow{B}d\xrightarrow{P}c aA bB dP c 即为一条所求路径。

    • 引理 4:对于非平凡点双 S S S 和任意 S S S 内的一点 u u u 和一边 e e e,存在一个经过 u , e u,e u,e 的简单环。

      证明:设 e = ( a , b ) e=(a,b) e=(a,b)。若 u = a u=a u=a u = b u=b u=b 则显然;否则,根据引理 3,存在一条简单路径 P P P 依次经过 a , u , b a,u,b a,u,b,显然 P P P 不经过 e e e,于是 P , e P,e P,e 组成的简单环即为一条所求的环。

    • 引理 5:对于点双 S S S 和任意 S S S 内两个不同的点 a , b a,b a,b 和一边 e e e,存在一条 a , b a,b a,b 间的简单路径经过 e e e

      证明:若 S S S 中只有两个点的情况那么证明显然,所以不妨设 S S S 是非平凡点双。

      e = ( u , v ) e=(u,v) e=(u,v)。根据引理 4,存在一个经过 a , e a,e a,e 的简单环 C C C,不妨将 C C C a − u a-u au 段称为 A A A a − v a-v av 段称为 B B B。如图:

      在这里插入图片描述

      任取一条从 b b b 出发不经过 a a a 的与 C C C 相交的简单路径 P P P(为什么一定能取到?考虑取一条 b , u b,u b,u 间的不经过 a a a 的简单路径即可)

      不妨设 P P P C C C 第一次相交在 A A A 上的点 c c c(那么 c ≠ a c\neq a c=a),于是 b → P c → A u → e v → B a b\xrightarrow{P}c\xrightarrow{A}u\xrightarrow{e}v\xrightarrow{B}a bP cA ue vB a 即为一条所求路径。

    引理 3 和引理 5 是两个很常用的结论。

    看回这题,考虑将原图缩成点双,那么若 x , y x,y x,y 间存在某个有 1 1 1 边的点双,那么根据引理 5, x , y x,y x,y 间就一定有经过 1 1 1 边的简单路径。反之,若 x , y x,y x,y 间所有点双都只有 0 0 0 边,那么 x , y x,y x,y 间的所有简单路径肯定都只包含 0 0 0 边。

    有了这个结论后剩下就好做了。具体地,先建出圆方树。然后对于原图上一条 0 0 0 ( u , v ) (u,v) (u,v),它恰属于一个点双 S S S,找到 S S S 对应的方点只需在圆方树上根据 u , v u,v u,v 相对位置讨论即可,然后我们再给该方点打上标记。接下来统计圆方树上不经过标记点的圆点间路径数即可。

    于是我们解决了 “ ( x , y ) (x,y) (x,y) 之间所有简单路径都只经过 0 0 0 边的点对数量”,对于只经过 1 1 1 边的同理统计。

    接下来统计 ( x , y ) (x,y) (x,y) 之间既有只经过 0 0 0 边的路径(称为 0 0 0 路)、又有只经过 1 1 1 边的路径(称为 1 1 1 路)的点对数量,称这种点对为 “合法点对”。

    • 命题 6:若 ( x , y ) (x,y) (x,y) 为合法点对,那么 x , y x,y x,y 之间的任意一条 0 0 0 路和任意一条 1 1 1 路都不交(除 x , y x,y x,y 外)。

      证明:否则可以调整得到一条 01 01 01 路。

    • 推论 7:若 ( x , y ) (x,y) (x,y) 为合法点对,那么 x , y x,y x,y 肯定同在一个点双 S S S 内,进一步地, S S S 肯定是非平凡点双。

    考虑对某个非平凡点双 S S S 统计其内的合法点对 ( x , y ) (x,y) (x,y) 的数量。

    一个直觉(出题人说有,但我没有,怎么会事呢?)是,点双 S S S 肯定长成这样:

    在这里插入图片描述

    接下来我们来证明这一点。

    我们称 S S S 内的一点 u u u 为交错点,当且仅当 u u u S S S 内既存在相邻的 0 0 0 边,也存在相邻的 1 1 1 边。

    • 命题 8:若 ( x , y ) (x,y) (x,y) S S S 内的合法点对。对于 S S S 内的任意交错点 u u u x , y x,y x,y 中必有一者等于 u u u

      证明:反证,若 x , y , u x,y,u x,y,u 为三个不同的点。设与 u u u 相邻的 0 0 0 边为 ( u , a ) (u,a) (u,a),与 u u u 相邻的 1 1 1 边为 ( u , b ) (u,b) (u,b)。根据引理 5,存在两条 x , y x,y x,y 间简单路径,其中一条 A A A 经过 ( u , a ) (u,a) (u,a),其中一条 B B B 经过 ( u , b ) (u,b) (u,b)。如图:

      在这里插入图片描述

      由于 ( x , y ) (x,y) (x,y) 为合法点对,那么可知 A A A 0 0 0 路且 B B B 1 1 1 路,但它们相交在点 u u u,根据命题 6 可知矛盾。

    • 推论 9:若 S S S 内存在合法点对,则 S S S 内至多有 2 2 2 个交错点。

    • 推论 10:若 S S S 内存在合法点对,则 S S S 内恰有 2 2 2 个交错点。

      证明:若 S S S 只有一个交错点,容易证明该交错点是割点,矛盾。

    • 命题 11:若 S S S 内恰有 2 2 2 个交错点 x , y x,y x,y,那么 S S S 内恰有一对合法点对 ( x , y ) (x,y) (x,y)

      证明:对于任意 S S S 内的合法点对 ( x ′ , y ′ ) (x',y') (x,y),根据命题 8 可知, x ′ , y ′ x',y' x,y 中必有一者等于 x x x,且必有一者等于 y y y,那么 ( x ′ , y ′ ) = ( x , y ) (x',y')=(x,y) (x,y)=(x,y)

      从而只需证明 ( x , y ) (x,y) (x,y) 为合法点对即可:根据引理易知, x , y x,y x,y 之间肯定既有包含 0 0 0 边的路径、也有包含 1 1 1 边的路径,于是只需证明 x , y x,y x,y 间不存在既包含 0 0 0 边也包含 1 1 1 边的路径即可。而若出现了这种路径,显然能找到另一个不同于 x , y x,y x,y 的交错点,矛盾。

    于是,我们证明了,某个非平凡点双 S S S 内包含合法点对,当且仅当 S S S 中恰有 2 2 2 个交错点,且此时 S S S 中恰包含一个合法点对。

    于是我们可以线性处理整个问题。具体地,对于原图上一条边 ( u , v ) (u,v) (u,v),找到其所在点双在圆方树上对应点 S S S,我们给树上 ( u , S ) , ( v , S ) (u,S),(v,S) (u,S),(v,S) 这两条边分别标上 ( u , v ) (u,v) (u,v) 所属颜色的记号,那么最后若 ( u , S ) (u,S) (u,S) 上既有 0 0 0 记号又有 1 1 1 记号,就意味着 u u u S S S 内的交错点。

    ll C2(int x){return 1ll*x*(x-1)/2;}
    
    struct edge
    {
    	int u,v;
    	bool c;
    }e[M];
    
    int n,m;
    int top,sta[N];
    int idx,dfn[N],low[N];
    int node,fa[N<<1],dep[N<<1];
    int tag[N<<1],tot[N<<1];
    ll ans;
    
    vector<int> e1[N],e2[N<<1];
    
    void tarjan(int u)
    {
    	dfn[u]=low[u]=++idx;
    	sta[++top]=u;
    	for(int v:e1[u])
    	{
    		if(!dfn[v])
    		{
    			tarjan(v);
    			low[u]=min(low[u],low[v]);
    			if(low[v]>=dfn[u])
    			{
    				int S=++node,w;
    				do
    				{
    					w=sta[top],top--;
    					e2[S].push_back(w),e2[w].push_back(S);
    				}while(w!=v);
    				e2[S].push_back(u),e2[u].push_back(S);
    			}
    		}
    		else low[u]=min(low[u],dfn[v]);
    	}
    }
    
    void dfs(int u)
    {
    	for(int v:e2[u]) if(v!=fa[u])
    		fa[v]=u,dep[v]=dep[u]+1,dfs(v);
    }
    
    int findS(int u,int v)
    {
    	return fa[dep[u]<dep[v]?v:u];
    }
    
    int dfs1(int u)
    {
    	int size=(u<=n);
    	for(int v:e2[u]) if(v!=fa[u])
    		tag[u]?(ans+=C2(dfs1(v))):(size+=dfs1(v));
    	return size;
    }
    
    void calc1(bool c)
    {
    	for(int i=1;i<=m;i++)
    		if(e[i].c^c) tag[findS(e[i].u,e[i].v)]=1;
    	ans+=C2(dfs1(1));
    	for(int i=1;i<=node;i++) tag[i]=0;
    }
    
    void calc2()
    {
    	for(int i=1;i<=m;i++)
    	{
    		int S=findS(e[i].u,e[i].v);
    		tag[dep[e[i].u]<dep[S]?S:e[i].u]|=(1<<e[i].c);
    		tag[dep[e[i].v]<dep[S]?S:e[i].v]|=(1<<e[i].c);
    	}
    	for(int i=1;i<=node;i++)
    		if(tag[i]==3) tot[i]++,tot[fa[i]]++;
    	for(int i=n+1;i<=node;i++)
    		if(tot[i]==2) ans++;
    }
    
    int main()
    {
    	read(),n=node=read(),m=read();
    	for(int i=1;i<=m;i++)
    	{
    		e[i].u=read(),e[i].v=read(),e[i].c=(getchar()=='D');
    		e1[e[i].u].push_back(e[i].v),e1[e[i].v].push_back(e[i].u);
    	}
    	tarjan(1);
    	dfs(1);
    	calc1(0);
    	calc1(1);
    	calc2();
    	printf("%lld\n",C2(n)-ans);
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
  • 相关阅读:
    算法刷题打卡第17天:全局倒置与局部倒置
    世界各国当日数据探索性分析
    高效、优雅的对象copy之MapStruct入门到精通,实战踩坑版
    【微服务部署】九、使用Docker Compose搭建高可用双机热备MySQL数据库
    判断链表是否是回文链表
    python使用%操作符进行字符串格式化
    XShelll-修改快捷键-xftp-修改编辑器
    Iterator设计模式
    git中如何在父仓库提交子仓库的修改
    写在大一结束
  • 原文地址:https://blog.csdn.net/ez_lcw/article/details/126899206