CF36E Two Paths
XXXV.CF36E Two Paths
为什么这题会被归到数据结构博客里呢?因为我的代码使用了大剂量的 STL
。
我吹爆 list
有没有!再也不手写链表了(并不),但是在欧拉路问题上真的贼好用!
首先,覆盖所有边恰一次,妥妥的欧拉路模型。
然后就先考虑如何判无解了。怎样无解呢?
-
有少于 条边。(如果不是样例给了,大概很难注意到……)
-
有超过 个连通块。
-
仅有一个连通块,且连通块中奇点数大于 。
-
有两个连通块,且其中某一个块中奇点数大于 。
那么,是否所有条件都满足,就一定有解呢?
是的。
假如从欧拉路的方向考虑,就会非常麻烦,因为你找出的欧拉路删去后可能使得这张图分成许多不连通的图。
因此,这里有一个很好的思路:在两个奇点间连边,这样就会转换为欧拉回路,然后在欧拉回路上断去新加的边就行了。
具体而言,对于两个连通块的情形,显然上述结论正确,因为每个连通块都必然符合条件。
然后,对于仅有一个连通块的情形,其中可能有 个, 个或是 个奇点。
个奇点就搜出回路然后随便砍两刀断环成链即可。
个奇点就在额外连的那条边处砍一刀,然后再随便砍一刀就行。
个奇点这两刀必须砍在额外的两条边的位置。这形成的两条链必然非空,因为这两条新边必然无公共端点,即其在欧拉环上必然不相邻。
时间复杂度 。
致死量 STL
的代码:
#include<bits/stdc++.h>
using namespace std;
int m,n,X[10010],Y[10010],dsu[20010],lim,id[20010];
bool deg[20010],used[10010];
vector<int>v,u[20010],w[20010];
int find(int x){return dsu[x]==x?x:dsu[x]=find(dsu[x]);}
void merge(int x,int y){
x=find(x),y=find(y);
if(x==y)return;
dsu[y]=x;
}
#define lst list<int>::iterator
list<int>ls;
void dfs(lst pos,int x){
while(true){
while(!w[x].empty()&&used[w[x].back()])w[x].pop_back();
if(w[x].empty())return;
int i=w[x].back(),y=X[i]^Y[i]^x;w[x].pop_back(),used[i]=true;
dfs(ls.insert(pos,i),y);
}
}
vector<int>s[3];
int main(){
freopen("input.txt","r",stdin);
freopen("output.txt","w",stdout);
scanf("%d",&m);
for(int i=1;i<=m;i++)scanf("%d%d",&X[i],&Y[i]),v.push_back(X[i]),v.push_back(Y[i]);
if(m==1){puts("-1");return 0;}
sort(v.begin(),v.end()),v.resize(n=unique(v.begin(),v.end())-v.begin());
for(int i=1;i<=n;i++)dsu[i]=i;
for(int i=1;i<=m;i++)merge(X[i]=lower_bound(v.begin(),v.end(),X[i])-v.begin()+1,Y[i]=lower_bound(v.begin(),v.end(),Y[i])-v.begin()+1),deg[X[i]]^=1,deg[Y[i]]^=1;
// for(int i=1;i<=m;i++)printf("%d %d\n",X[i],Y[i]);
// for(int i=1;i<=n;i++)printf("(%d,%d)",find(i),deg[i]);puts("");
for(int i=1;i<=n;i++)if(dsu[i]==i)id[i]=++lim;
for(int i=1;i<=n;i++)u[id[find(i)]].push_back(i);
if(lim>=3){puts("-1");return 0;}
for(int i=1;i<=m;i++)w[X[i]].push_back(i),w[Y[i]].push_back(i);
if(lim==1){
vector<int>sp;
for(auto i:u[1])if(deg[i])sp.push_back(i);
if(sp.size()>4){puts("-1");return 0;}
for(int i=0;i<sp.size();i+=2){
int eid=m+i/2+1;
X[eid]=sp[i],Y[eid]=sp[i+1],w[sp[i]].push_back(eid),w[sp[i+1]].push_back(eid);
}
dfs(ls.begin(),u[1].back());
// printf("%d\n",sp.size());
if(sp.empty()){
s[1].push_back(ls.front()),ls.pop_front();
for(auto i:ls)s[2].push_back(i);
}
if(sp.size()==2){
while(ls.front()<=m)ls.push_back(ls.front()),ls.pop_front();
ls.pop_front();
s[1].push_back(ls.front()),ls.pop_front();
while(!ls.empty())s[2].push_back(ls.front()),ls.pop_front();
}
if(sp.size()==4){
while(ls.front()<=m)ls.push_back(ls.front()),ls.pop_front();
ls.pop_front();
while(ls.front()<=m)s[1].push_back(ls.front()),ls.pop_front();
ls.pop_front();
while(!ls.empty())s[2].push_back(ls.front()),ls.pop_front();
}
}
if(lim==2){
for(int i=1;i<=2;i++){
ls.clear();
vector<int>sp;
for(auto j:u[i])if(deg[j])sp.push_back(j);
if(sp.size()>2){puts("-1");return 0;}
if(!sp.empty())X[m+1]=sp[0],Y[m+1]=sp[1],w[sp[0]].push_back(m+1),w[sp[1]].push_back(m+1),used[m+1]=false;
dfs(ls.begin(),u[i].back());
if(!sp.empty()){while(ls.front()<=m)ls.push_back(ls.front()),ls.pop_front();ls.pop_front();}
while(!ls.empty())s[i].push_back(ls.front()),ls.pop_front();
}
}
for(int i=1;i<=2;i++){
printf("%d\n",s[i].size());
for(auto j:s[i])printf("%d ",j);puts("");
}
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
· 【杂谈】分布式事务——高大上的无用知识?