首页 > 其他分享 >洛谷 P8456 -「SWTR-8」地地铁铁(图论+结论)

洛谷 P8456 -「SWTR-8」地地铁铁(图论+结论)

时间:2023-04-19 15:57:18浏览次数:59  
标签:P8456 洛谷 int 路径 SWTR ec stk ++ MAXN

挺有意思的结论题,结论的证明比较复杂。据出题人说他大概想了几天几夜才证出来,所以本篇题解并不详细给出结论证明,如果有兴趣可以自己去看出题人的题解:https://www.luogu.com.cn/blog/AlexWei/solution-p8456。

首先涉及到简单路径,肯定往双连通分量的方向思考。因此我们首先建出圆方树,并将所有可能的点对分为在同一个点双内和不在同一个点双内的情况处理:

  • 先考虑在同一个点双内的情况,显然如果点双里所有边颜色都相同就似了,这种情况下点双内任意一对点对间都不存在经过两种颜色边的路径。如果两种颜色都有,你的第一反应肯定感觉是任意点对间都存在符合条件的路径,但稍微想想就可以找出反例:长度为 3 的环,两个 D 一个 d,这样 D 和 d 分界处两个点之间不存在符合要求的路径。思考这样的情况出现的原因:点双里总共只有两个点同时存在 D 与 d 的出边,这样你要么一直待在 d 的区域,要么一直待在 D 的区域,如果要从一个区域切换到另一个区域就必须经过这两个点,这样就违背了简单路径。那么是否只有这一个反例呢?答案是肯定的,也就是说如果一个点双里有且只有两个点满足其既有 D 也有 d 的出边,那么其对答案的贡献就是 \(\dbinom{siz}{2}-1\),否则贡献 \(\dbinom{siz}{2}\)。比较感性的理解方式是你可以走到另一个存在两种颜色出边的点在那里切换颜色。
  • 再考虑不在同一个点双内的情况。称所有边都是 D 的点双为黑点双,所有边都是 d 的点双为白点双,既存在 D 又存在 d 的点双为混色点双,那么结论是如果两点在圆方树上的路径上全是黑点双,或者全是白点双的时候才不合法。因为如果既存在黑点双又存在白点双,那么显然是合法的,否则其至少存在一个混色点双,根据上一部分的结论,对于一个混色点双而言,最坏的情况就是上文所说的不合法的点对,这种情况下两点之间存在只由 D 组成的路径,也存在只由 d 组成的路径,但是不存在包含 D 和 d 的路径,但是由于我们经过了至少一个点双,也就是我们可以通过其他部分的包含 D 还是包含 d,来决定这两点之间我们是选择只包含 D 的路径,还是只包含 d 的路径,这样上述点对必然合法,直接并查集求即可。
const int MAXN=5e6;
int n,m,hd[MAXN+5],to[MAXN+5],nxt[MAXN+5],val[MAXN+5],ec=1;
vector<int>g[MAXN+5];int typ[MAXN+5];ll res;
void adde(int u,int v,int w){
	to[++ec]=v;val[ec]=w;nxt[ec]=hd[u];hd[u]=ec;
	to[++ec]=u;val[ec]=w;nxt[ec]=hd[v];hd[v]=ec;
}
int dfn[MAXN+5],low[MAXN+5],tim=0,stk[MAXN+5],tp=0;
int e_stk[MAXN+5],e_top,ncnt,in[MAXN+5];
void tarjan(int x){
	dfn[x]=low[x]=++tim;stk[++tp]=x;
	for(int e=hd[x];e;e=nxt[e]){
		int y=to[e];
		if(!dfn[y]){
			e_stk[++e_top]=e>>1;in[e>>1]=1;
			tarjan(y);chkmin(low[x],low[y]);
			if(low[y]>=dfn[x]){
				vector<int>V,E;
				int o;++ncnt;
				do{o=stk[tp--];V.pb(o);}while(o^y);V.pb(x);
				do{o=e_stk[e_top--];in[o]=0;E.pb(o);}while(o^(e>>1));
				static int msk[MAXN+5];
				for(int z:V)g[ncnt].pb(z),g[z].pb(ncnt),msk[z]=0;
				int totmsk=0,sum=0;
				for(int z:E){
					totmsk|=(1<<val[z<<1]);
					msk[to[z<<1]]|=(1<<val[z<<1]);
					msk[to[z<<1|1]]|=(1<<val[z<<1]);
				}
				typ[ncnt]=totmsk-1;
				for(int z:V)sum+=(msk[z]==3);
				if(sum==2)res--;
			}
		}else{
			chkmin(low[x],dfn[y]);
			if(dfn[y]<dfn[x]&&!in[e>>1])e_stk[++e_top]=e>>1;
		}
	}
}
int f[MAXN+5],siz[MAXN+5];
int find(int x){return (!f[x])?x:f[x]=find(f[x]);}
void merge(int x,int y){x=find(x);y=find(y);f[x]=y;siz[y]+=siz[x];}
int main(){
	scanf("%*d%d%d",&n,&m);res=1ll*n*(n-1)/2;
	for(int i=1,u,v;i<=m;i++){
		static char buf[6];scanf("%d%d%s",&u,&v,buf+1);
		adde(u,v,buf[1]=='D');adde(v,u,buf[1]=='D');
	}ncnt=n;tarjan(1);
	for(int i=1;i<=n;i++)siz[i]=1;
	for(int i=n+1;i<=ncnt;i++)if(typ[i]==0)for(int y:g[i])merge(i,y);
	for(int i=1;i<=ncnt;i++)if(find(i)==i)res-=1ll*siz[i]*(siz[i]-1)/2;
	memset(f,0,sizeof(f));memset(siz,0,sizeof(siz));
	for(int i=1;i<=n;i++)siz[i]=1;
	for(int i=n+1;i<=ncnt;i++)if(typ[i]==1)for(int y:g[i])merge(i,y);
	for(int i=1;i<=ncnt;i++)if(find(i)==i)res-=1ll*siz[i]*(siz[i]-1)/2;
	printf("%lld\n",res);
	return 0;
}

标签:P8456,洛谷,int,路径,SWTR,ec,stk,++,MAXN
From: https://www.cnblogs.com/tzcwk/p/luogu-P8456.html

相关文章

  • 洛谷P1249最大乘积,数论找规律
    最大乘积题目描述一个正整数一般可以分为几个互不相同的自然数的和,如\(3=1+2\),\(4=1+3\),\(5=1+4=2+3\),\(6=1+5=2+4\)。现在你的任务是将指定的正整数\(n\)分解成若干个互不相同的自然数的和,且使这些自然数的乘积最大。输入格式只一个正整数\(n\),(\(3\leqn\leq10000\))。......
  • 洛谷P5494 【模板】线段树分裂
    传送门  需要的前置知识:线段树合并。  感觉会了线段树合并这个就很简单,线段树分裂就是在把一颗权值线段树值域在[x,y]的区间分裂出来单独成一个线段树,那么我们只需要从新树q和旧树p的根节点一起走,如果走到当前p被[x,y]完全包含的路径就把p的编号给q,并且把p改为0就行了,注意......
  • 洛谷 p1102 A-B数对
    题目背景出题是一件痛苦的事情!相同的题目看多了也会有审美疲劳,于是我舍弃了大家所熟悉的A+BProblem,改用A-B了哈哈!题目描述给出一串正整数数列以及一个正整数 C,要求计算出所有满足A−B=C 的数对的个数(不同位置的数字一样的数对算不同的数对)。输入格式输入共两行。......
  • 洛谷P7492 [传智杯 #3 决赛] 序列 题解 数列分块
    题目链接:https://www.luogu.com.cn/problem/P7492解题思路:分块。解题思路全部来自yzy1大佬的博客额外掌握技能:编译时加入-Wall参数。示例程序:#include<bits/stdc++.h>usingnamespacestd;constintmaxn=1e5+5;intn,m,blo,//n表示数列长度,m表......
  • 洛谷T226686 长度为2的子串
    题目描述给你一个长度为n 的由大写的英文字母组成的字符串,请你找出出现频率最高的长度为2的子串。输入格式包括两行。第一行是一个正整数n,表示字符串长度。第二行是长度为n的大写英文字母组成的字符串。(2<=n<=100)输出格式包括一行。一个长度为2的字符串,该字符串为输入......
  • 【rmq】洛谷P7333
    题目:P7333[JRKSJR1]JFCA-洛谷|计算机科学教育新生态(luogu.com.cn)分析:用rmq处理出各个区间长度的最大值,然后在二分区间长度找到答案(最开始想的是开长度为n的数组,对位置i的数,分别找1-(i-1)和(i+1)-n中的离i最近满足条件的位置,然后更新结果,但一直wa,还没找到问题,存疑吧)代码:......
  • 洛谷 P3292 [SCOI2016]幸运数字
    https://www.luogu.com.cn/problem/P3292多次询问求一条链取若干点的最大异或和考虑一个集合的最大异或和可以求出线性基完成,两个集合的线性基可以合并,但是线性基并没有可减性,于是我们求lca的时候只能每次往集合里添加一条链,为了保证复杂度只能用倍增做。std::vector<i64>......
  • w1 洛谷T233243
      主要思路就是计算每一个长度为2的子串出现的次数,计数的同时用数组记录次数,打擂台找到出现次数最多的子串,首字符出现的位置就是数组的下标。最后输出出现最多的子串。代码如下:#include<bits/stdc++.h>usingnamespacestd;intcnt[100];intmain(){ intn,max=-1; ......
  • 洛谷P2415 集合求和(数学问题,使用集合子集求和公式)
    可以知道对于一个有n个数据的集合,其子集个数有2^n个至于证明可以这样理解,对于n个数据,其子集就是对数据进行组和,而对于每个位置上的数据,组合时仅有两种状态即有此数据或无此数据,也就是有两种可能,而对于n个数据,就有2^n种可能不妨设其中一个非空数据X,对于X,依据X可以将子集划分为两......
  • 洛谷与 Codeforces 的难度评级
    为了比较CF与洛谷的题目难度评级,我写了一个爬虫,爬取了CF题目在源站和洛谷中的难度,并进行比较。这里先给出两者的换算:洛谷入门普及-普及/提高-普及+/提高提高+/省选-省选/NOI-NOI/NOI+/CTSCCF800900-11001200-15001600-19002000-23002400-29003000-350......