拆边的故事

感觉“拆边”这个说法实在不专业啊,但也只能这样了,意思到了就行了。

概述

在图论中,有的时候暴力建边的话,可能会得到一个近似于完全图的图,边数会达到 N2 级别,这显然是不被允许的。往往这些题目,要么就不是用图论解决的,要么就是有隐藏结论可以删边(比如某玄学题目解法里就用到了奇怪的删边技巧,但盲猜这种奇技淫巧不会在大赛里出现),再就是运用一些拆边技巧了。

拆边的原理就是把原来的一条边拆成几条边,这样看似边数变多了,但往往原图里的那些边拆完之后有公共部分,拆出来的边不会特别多,以达到降低复杂度的目的。

目前做到的题目,用到拆边的,一般都是图内节点两两可达,而且边权还是用一些奇怪的公式加上两个点自身具有的一些参数计算出来的,这些情况下就可以拆边。

T1

首先说明题非原创,搬运而来,背景嘛倒是我自己写的。

题目重点就是 dis=min(|xixj|,|yiyj|) ,发现它符合上面的第二个性质“边权还是用一些奇怪的公式加上两个点自身具有的一些参数计算出来的”(自己体会什么意思吧),然后题目描述也符合“两两可达”的性质,那么可以想到拆边。

解法就是先按x坐标排序,给相邻两个点连一条无向边,边权 xixi1 ;再按y坐标排序,然后连边。至于取min,跑最短路的时候自然会取。这样,我们就把 N2条 的边数优化到了 N 条。

#include<cstdio>
#include<queue>
#include<algorithm>
#include<cstring>
#define ll long long
using namespace std;
const int N=200010;
inline void read(int &wh){
    wh=0;int f=1;char w=getchar();
    while(w<'0'||w>'9'){if(w=='-')f=-1;w=getchar();}
    while(w<='9'&&w>='0'){wh=wh*10+w-'0';w=getchar();}
    wh*=f;return;
}

inline int min(int s1,int s2){
	return s1<s2?s1:s2;
}
inline int abs(int s1){
	return s1<0?-s1:s1;
}
int m;
struct point{
	int num,x,y;
}p[N];

struct edge{
	int t,next;
	ll v;
}e[N<<3];
int esum,head[N];
inline void add(int fr,int to,int val){
	esum++;
	e[esum].t=to;
	e[esum].v=val;
	e[esum].next=head[fr];
	head[fr]=esum;
}

inline bool cmp1(point s1,point s2){
	return s1.x<s2.x;
}
inline bool cmp2(point s1,point s2){
	return s1.y<s2.y;
}

ll dis[N];
bool vis[N];
struct node{
	int num;
	ll dis;
};
inline bool operator <(node s1,node s2){
	return s2.dis<s1.dis;
}
priority_queue<node>q;

signed main(){
	
	read(m);
	for(int i=1;i<=m;i++){
		p[i].num=i;
		read(p[i].x);read(p[i].y);
	}
	
	sort(p+1,p+m+1,cmp1);
	for(int i=1;i<m;i++){
		add(p[i].num,p[i+1].num,p[i+1].x-p[i].x);
		add(p[i+1].num,p[i].num,p[i+1].x-p[i].x);
	}
	sort(p+1,p+m+1,cmp2);
	for(int i=1;i<m;i++){
		add(p[i].num,p[i+1].num,p[i+1].y-p[i].y);
		add(p[i+1].num,p[i].num,p[i+1].y-p[i].y);
	}
	
	memset(dis,0x3f,sizeof(dis));
	dis[1]=0;
	q.push((node){1,0});
	while(!q.empty()){
		node now=q.top();q.pop();
		int wh=now.num;ll nd=now.dis;
		if(vis[wh])continue;vis[wh]=true;
		for(int i=head[wh],th;i;i=e[i].next){
			th=e[i].t;
			if(dis[wh]+e[i].v<dis[th]){
			    dis[th]=dis[wh]+e[i].v;
			    q.push((node){th,dis[th]});
			}
		}
	}
	printf("%lld",dis[m]);
	
	return 0;
}

T2

同样,理论上来说这个图里两两可达,而距离 |ij| 也满足条件,然后就可以拆边,方法应该不难想。至于题目中的限制条件,那是分层图干的事。

#include<cstdio>
#include<cstring>
#include<queue>
#define id(s1,s2) (s1*m+s2)
//#define zczc
using namespace std;
const int N=50010;
const int M=52;
const int S=N*M;
inline void read(int &wh){
    wh=0;int f=1;char w=getchar();
    while(w<'0'||w>'9'){if(w=='-')f=-1;w=getchar();}
    while(w<='9'&&w>='0'){wh=wh*10+w-'0';w=getchar(); }
    wh*=f;return;
}
inline bool get(){
	char w=getchar();
	while(w!='0'&&w!='1')w=getchar();
	return w=='1';
}

int m,n,a[N],b[M][M];
struct edge{
	int t,v,next;
}e[S<<2];
int esum,head[S];
inline void add(int fr,int to,int val){
	esum++;
	e[esum].t=to;
	e[esum].v=val;
	e[esum].next=head[fr];
	head[fr]=esum;
}

int dis[S];
bool vis[S];
struct node{
	int wh,dis;
};
bool operator <(node s1,node s2){
	return s2.dis<s1.dis; 
}
priority_queue<node>q;

signed main(){
	
	#ifdef zczc
	freopen("in.txt","r",stdin);
	#endif
	
	read(m);read(n);
	for(int i=1;i<=m;i++)read(a[i]);
	for(int i=1;i<=n;i++){
		for(int j=1;j<=n;j++){
			b[i][j]=get();
		}
	}
	for(int i=1;i<=m;i++)add(id(0,i),id(a[i],i),0);
	for(int i=1;i<=n;i++){
		for(int j=1;j<=m;j++){
			if(b[i][a[j]]){
				add(id(i,j),id(0,j),0);
			}
		}
	}
	for(int i=1;i<=n;i++){
		for(int j=1;j<m;j++){
			add(id(i,j),id(i,j+1),1);
			add(id(i,j+1),id(i,j),1);
		}
	}
	memset(dis,0x3f,sizeof(dis));
	dis[id(0,1)]=0;q.push((node){id(0,1),0});
	while(!q.empty()){
		int wh=q.top().wh,nd=q.top().dis;q.pop();
		if(vis[wh])continue;vis[wh]=true;
		for(int i=head[wh],th;i;i=e[i].next){
			th=e[i].t;
			if(dis[wh]+e[i].v<dis[th]){
				dis[th]=dis[wh]+e[i].v;
				q.push((node){th,dis[th]});
			}
		}
	}
	printf("%d",dis[id(0,m)]>1e8?-1:dis[id(0,m)]);
	
	return 0;
}
posted @   Feyn618  阅读(72)  评论(0编辑  收藏  举报
编辑推荐:
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· AI技术革命,工作效率10个最佳AI工具
点击右上角即可分享
微信分享提示