提高组图论专题 2

news/2024/10/24 22:08:58/文章来源:https://www.cnblogs.com/laoshan-plus/p/18501432

提高组图论专题 2

T1 [NOIP2013 提高组] 华容道

首先,暴力 BFS 即可 AC,但要注意以下几点:

  • 队列必须手写;
  • \(\text{vis}\) 数组剪枝。

然后我们来看正解。参考这位的 DFS 思路,发现将暴力 DFS 改成每次让起点移动,具体过程为空格移动到起点、起点移动到空格,且在这个过程中空格不能经过起点。所以预处理从 \(a\) 点到 \(b\) 点不经过 \(c\) 的距离,考虑到这样会超时,发现起点一定挨着起点将要移动的位置,所以对于每个点只需处理相邻 4 点。

#include<bits/stdc++.h>
#define fw fwrite(obuf,p3-obuf,1,stdout)
#define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<20,stdin),p1==p2)?EOF:*p1++)
#define putchar(x) (p3-obuf<1<<20?(*p3++=(x)):(fw,p3=obuf,*p3++=(x)))
using namespace std;char buf[1<<20],obuf[1<<20],*p1=buf,*p2=buf,*p3=obuf,str[20<<2];
int read(){int x=0;char ch=getchar();while(!isdigit(ch))ch=getchar();while(isdigit(ch))x=(x<<3)+(x<<1)+(ch^48),ch=getchar();return x;
}
template<typename T>
void write(T x,char sf='\n'){if(x<0)putchar('-'),x=~x+1;int top=0;do str[top++]=x%10,x/=10;while(x);while(top)putchar(str[--top]+48);if(sf^'#')putchar(sf);
}
constexpr int MAXN=35;
constexpr int dx[4]={1,0,-1,0},dy[4]={0,1,0,-1};
int n,m,Q,a[MAXN][MAXN],ex,ey,sx,sy,tx,ty,ans;
int vis[MAXN][MAXN][MAXN][MAXN];
int dis[MAXN][MAXN][MAXN][MAXN][4];
bool ins[MAXN][MAXN];
struct Node{int sx,sy;bool operator<(const Node&x)const{return sx<x.sx;}
};
void dijkstra(int ex,int ey,int sx,int sy,int typ){memset(ins,0,sizeof(ins));priority_queue<pair<int,Node>>q;q.emplace(dis[ex][ey][ex][ey][typ]=0,(Node){ex,ey});while(!q.empty()){Node tp=q.top().second;q.pop();if(ins[tp.sx][tp.sy])continue;ins[tp.sx][tp.sy]=1;for(int i=0,xx,yy;i<4;++i){xx=tp.sx+dx[i],yy=tp.sy+dy[i];if(xx<1||xx>n||yy<1||yy>m||!a[xx][yy]||(xx==sx&&yy==sy))continue;if(dis[ex][ey][xx][yy][typ]>dis[ex][ey][tp.sx][tp.sy][typ]+1){dis[ex][ey][xx][yy][typ]=dis[ex][ey][tp.sx][tp.sy][typ]+1;q.emplace(-dis[ex][ey][xx][yy][typ],(Node){xx,yy});}}}
}
void dfs(int ex,int ey,int sx,int sy,int res){if(res>=ans)return;if(sx==tx&&sy==ty)return ans=min(ans,res),void();if(vis[ex][ey][sx][sy]<=res)return;vis[ex][ey][sx][sy]=res;for(int i=0,xx,yy;i<4;++i){xx=sx+dx[i],yy=sy+dy[i];if(xx<1||xx>n||yy<1||yy>m||!a[xx][yy])continue;dfs(sx,sy,xx,yy,res+dis[xx][yy][ex][ey][(i+2)%4]+1);}
}int main(){n=read(),m=read(),Q=read();for(int i=1;i<=n;++i)for(int j=1;j<=m;++j)a[i][j]=read();memset(dis,0x3f,sizeof(dis));for(int i=1;i<=n;++i)for(int j=1;j<=m;++j){if(!a[i][j])continue;for(int k=0,xx,yy;k<4;++k){xx=i+dx[k],yy=j+dy[k];if(xx<1||xx>n||yy<1||yy>m||!a[xx][yy])continue;dijkstra(i,j,xx,yy,k);}}while(Q--){memset(vis,0x3f,sizeof(vis));ex=read(),ey=read(),sx=read(),sy=read(),tx=read(),ty=read(),ans=0x3f3f3f3f;dfs(ex,ey,sx,sy,0);write(ans==0x3f3f3f3f?-1:ans);}return fw,0;
}

题解说这样写有 85pts,但实际上我直接 AC 了,不知是不是我常数小的问题。

题水跟我有什么关系

T2 Star Way To Heaven

《路径上最小距离最大值》,很难不让人想到最小生成树。考虑到将所有 \(\text{Star}\)两两间最短距离排序后依次连线,则最终我们的小 W 的最优答案一定是这些线的长度中最长的那条的长度除以 2

这个两两间最短距离就很灵性,我们考虑求出这个两两间最短距离。所以可以用 Prim 算法跑一遍,得到最短路径(即最小生成树),然后统计答案即可。

注意这里不能用队列来维护,因为这样统计到的答案会不全。发现 \(k\) 只有 \(6000\),所以 \(O(k^2)\) 是不成问题的,就不要像我一样老想着队列了。

#include<bits/stdc++.h>
#define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<20,stdin),p1==p2)?EOF:*p1++)
using namespace std;char buf[1<<20],*p1=buf,*p2=buf;
int read(){int x=0;char ch=getchar();while(!isdigit(ch))ch=getchar();while(isdigit(ch))x=(x<<3)+(x<<1)+(ch^48),ch=getchar();return x;
}
using pii=pair<int,int>;
using ld=long double;
constexpr int MAXN=6005;
int m,n,k,pre[MAXN];
struct{int x,y;
}a[MAXN];
ld dis[MAXN],ans;
bool vis[MAXN];ld Dis(int x,int y){if(x>y)swap(x,y);if(!x||y==k+1)return abs(a[x].y-a[y].y)*0.5l;return hypot(a[x].x-a[y].x,a[x].y-a[y].y)*0.5l;
}
void prim(){for(int i=0;i<=k+1;++i)dis[i]=2e9;dis[0]=0;for(int i=0;i<=k+1;++i){int u=0;ld minn=2e9;for(int j=0;j<=k+1;++j)if(!vis[j]&&minn>dis[j])minn=dis[j],u=j;vis[u]=1;if(u==k+1)break;for(int j=0;j<=k+1;++j)if(!vis[j]&&j^u&&dis[j]>Dis(u,j)){dis[j]=Dis(u,j);pre[j]=u;}}
}
int main(){m=read(),n=read(),k=read();for(int i=1;i<=k;++i)a[i]={read(),read()};a[k+1].y=n;prim();int x=k+1;while(x)ans=max(ans,Dis(pre[x],x)),x=pre[x];printf("%.10Lf\n",ans);return 0;
}

T3 [CF1005F] Berland and the Shortest Paths

前置芝士:最短路径树

知道这个这个题就很简单了,由于边权为 \(1\),可以先跑 BFS 求出每个点所有能转移到它的边的集合,答案就是 \(\min\big(\prod\text{siz}_i,k\big)\)。题目要求输出可行解,于是跑个 DFS 输出就完了。

#include<bits/stdc++.h>
#define fw fwrite(obuf,p3-obuf,1,stdout)
#define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<20,stdin),p1==p2)?EOF:*p1++)
#define putchar(x) (p3-obuf<1<<20?(*p3++=(x)):(fw,p3=obuf,*p3++=(x)))
using namespace std;char buf[1<<20],obuf[1<<20],*p1=buf,*p2=buf,*p3=obuf,str[20<<2];
int read(){int x=0;char ch=getchar();while(!isdigit(ch))ch=getchar();while(isdigit(ch))x=(x<<3)+(x<<1)+(ch^48),ch=getchar();return x;
}
template<typename T>
void write(T x,char sf='\n'){if(x<0)putchar('-'),x=~x+1;int top=0;do str[top++]=x%10,x/=10;while(x);while(top)putchar(str[--top]+48);if(sf^'#')putchar(sf);
}
constexpr int MAXN=2e5+5;
int n,m,k,head[MAXN],tot=1;
struct{int v,to;}e[MAXN<<1];
void addedge(int u,int v){e[++tot]={v,head[u]};head[u]=tot;
}
vector<int>pre[MAXN];
int dis[MAXN],cnt,ans=1;
bool vis[MAXN];
void bfs(){memset(dis,0x3f,sizeof(int)*(n+5));dis[1]=0;queue<int,list<int>>q;q.emplace(1);while(!q.empty()){int u=q.front();q.pop();if(vis[u])continue;vis[u]=1;for(int i=head[u];i;i=e[i].to)if(dis[e[i].v]>dis[u]+1){dis[e[i].v]=dis[u]+1;pre[e[i].v].emplace_back(i);q.emplace(e[i].v);}else if(dis[e[i].v]==dis[u]+1)pre[e[i].v].emplace_back(i);}
}
void dfs(int u){if(u>n){for(int i=1;i<=m;++i)write(vis[i],'#');putchar('\n');if(++cnt==ans)fw,exit(0);return;}for(auto x:pre[u]){vis[x>>1]=1;dfs(u+1);vis[x>>1]=0;}
}int main(){n=read(),m=read(),k=read();for(int i=1,u,v;i<=m;++i){u=read(),v=read();addedge(u,v),addedge(v,u);}bfs();for(int i=2;i<=n;++i)if(ans*(long long)pre[i].size()>k){ans=k;break;}else ans*=pre[i].size();write(ans);memset(vis,0,sizeof(int)*(m+5));dfs(2);return fw,0;
}

T4 [JOI 2017 Final] 足球 / サッカー (Soccer)

分层图好题。

对于这种题面很长、转移很复杂的题目,显然不能 DP,考虑转化为分层图最短路问题。对于每个点 \((i,j)\),它需要设置五个状态:控球,以及向上/下/左/右踢球。为什么踢球要设置四个状态呢?因为球在被踢的过程中不能转向。

然后是本题的重点:连边

  1. 当前运球点向周围运球点连双向边,边权为 \(C\)
  2. 四个踢球点分别向相邻对应的点连单向边,边权为 \(A\)
    注意判断是否越界。
  3. 对于当前位置的五个点,当前运球点向当前四个踢球点连单向边,边权为 \(B\)
    注意这是很妙的一步,原本这个 \(B\) 的贡献是踢球踢出来的,现在将这个贡献提前到运球转踢球这个过程中。
  4. 对于当前位置的五个点,当前四个踢球点向当前运球点连单项边,边权为 \(\text{dis}\times C\)
    解释一下,因为这种连边方式需要最近的球员跑过来,所以这个 \(\text{dis}\) 是当前点到最近球员的距离。BFS 可以很容易地求出。

连完了所有边之后跑 Dijkstra 就行。注意起始点是 \((S_1,T_1)\) 而不是 \((1,1)\),结束点是 \((S_N,T_N)\) 而不是 \((H,W)\)

思路还是很清晰的,三步走:BFS、连边、Dijkstra。

#include<bits/stdc++.h>
#define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<20,stdin),p1==p2)?EOF:*p1++)
using namespace std;char buf[1<<20],*p1=buf,*p2=buf;
int read(){int x=0;char ch=getchar();while(!isdigit(ch))ch=getchar();while(isdigit(ch))x=(x<<3)+(x<<1)+(ch^48),ch=getchar();return x;
}
using ll=long long;
constexpr int MAXH=505,MAXN=1e5+5;
constexpr int dx[4]={-1,1,0,0},dy[4]={0,0,-1,1};
int h,w,a,b,c,n,S;
struct Node{int s,t;Node(int s=0,int t=0): s(s),t(t) {}
}s[MAXN];
int head[MAXH*MAXH*5],tot;
struct{int v,to;ll w;}e[MAXH*MAXH*50];
void addedge(int u,int v,ll w){e[++tot]={v,head[u],w};head[u]=tot;
}
ll dis[MAXH*MAXH*5];
bool vis[MAXH*MAXH*5];
queue<Node>q;
int R(int x,int y){return (x-1)*w+y;
}void bfs(){while(!q.empty()){int x=q.front().s,y=q.front().t;q.pop();for(int i=0,xx,yy,r;i<4;++i){xx=x+dx[i],yy=y+dy[i];if(xx<1||xx>h||yy<1||yy>w||vis[r=R(xx,yy)]||dis[r]<=dis[R(x,y)]+c)continue;vis[r]=1;dis[r]=dis[R(x,y)]+c;q.emplace(xx,yy);}}
}void add(){for(int x=1;x<=h;++x)for(int y=1;y<=w;++y){int r=R(x,y);addedge(r,r+S,b),addedge(r,r+S*2,b),addedge(r,r+S*3,b),addedge(r,r+S*4,b);addedge(r+S,r,dis[r]),addedge(r+S*2,r,dis[r]),addedge(r+S*3,r,dis[r]),addedge(r+S*4,r,dis[r]);// 0上1下2左3右for(int i=0,xx,yy;i<4;++i){xx=x+dx[i],yy=y+dy[i];if(xx<1||xx>h||yy<1||yy>w)continue;int t=R(xx,yy);addedge(r,t,c);switch(i){case 0: addedge(r+S,t+S,a);break;case 1: addedge(r+S*2,t+S*2,a);break;case 2: addedge(r+S*3,t+S*3,a);break;default: addedge(r+S*4,t+S*4,a);break;}}}
}void dijkstra(){priority_queue<pair<ll,int>>q;memset(dis,0x3f,sizeof(ll)*(h*w*5+5));memset(vis,0,sizeof(bool)*(h*w*5+5));q.emplace(dis[R(s[1].s,s[1].t)]=0,R(s[1].s,s[1].t));while(!q.empty()){int u=q.top().second;q.pop();if(vis[u])continue;vis[u]=1;for(int i=head[u];i;i=e[i].to)if(dis[e[i].v]>dis[u]+e[i].w){dis[e[i].v]=dis[u]+e[i].w;q.emplace(-dis[e[i].v],e[i].v);}} 
}int main(){h=read()+1,w=read()+1,a=read(),b=read(),c=read(),n=read();S=R(h,w);memset(dis,0x3f,sizeof(ll)*(h*w+5));for(int i=1,r;i<=n;++i){s[i]={read()+1,read()+1};r=R(s[i].s,s[i].t);dis[r]=0,vis[r]=1;q.emplace(s[i]);}bfs();add();dijkstra();const int F=R(s[n].s,s[n].t);printf("%lld\n",min({dis[F],dis[F+S],dis[F+S*2],dis[F+S*3],dis[F+S*4]}));return 0;
}

T5 [CF1981E] Turtle and Intersected Segments

发现瓶颈在于连边,考虑优化这个过程。发现对于三条两两相交的线段(设边权为 \(a_1,a_2,a_3\) 且 \(a_1\le a_2\le a_3\)),需要连的边只有两条:\((a_1,a_2)\)\((a_2,a_3)\)。因为易得 \((a_1,a_3)\) 一定不会出现在最小生成树上。

所以我们考虑这样的一个扫描线流程:每条线段在 \(l\) 处加入、在 \(r\) 处删除,\(O(n)\) 地扫描,每加入一条线段时向它的前驱、后继连边。用 multiset 维护即可做到 \(O(n\log n)\),而边数降到了 \(O(n)\),然后跑 Kruskal 就完了。

#include<bits/stdc++.h>
#define fw fwrite(obuf,p3-obuf,1,stdout)
#define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<20,stdin),p1==p2)?EOF:*p1++)
#define putchar(x) (p3-obuf<1<<20?(*p3++=(x)):(fw,p3=obuf,*p3++=(x)))
using namespace std;char buf[1<<20],obuf[1<<20],*p1=buf,*p2=buf,*p3=obuf,str[20<<2];
int read(){int x=0;char ch=getchar();while(!isdigit(ch))ch=getchar();while(isdigit(ch))x=(x<<3)+(x<<1)+(ch^48),ch=getchar();return x;
}
template<typename T>
void write(T x,char sf='\n'){if(x<0)putchar('-'),x=~x+1;int top=0;do str[top++]=x%10,x/=10;while(x);while(top)putchar(str[--top]+48);if(sf^'#')putchar(sf);
}
constexpr int MAXN=5e5+5;
int t,n;
struct{int l,r,a;}s[MAXN];
int b[MAXN<<1],tot,tt,fuc,cnt;
pair<int,int> c[MAXN<<1];
struct Edge{int u,v,w;bool operator<(const Edge&x)const{return w<x.w;}
}e[MAXN];
int f[MAXN];
int find(int x){if(f[x]^x)f[x]=find(f[x]);return f[x];
}
void kruskal(){iota(f+1,f+n+1,1);sort(e+1,e+fuc+1);long long ans=0;for(int i=1,fu,fv;i<=fuc;++i){fu=find(e[i].u),fv=find(e[i].v);if(fu==fv)continue;f[fv]=fu;ans+=e[i].w;if(++cnt==n-1)break;}write(cnt==n-1?ans:-1);
}int main(){t=read();while(t--){tot=tt=fuc=cnt=0;n=read();for(int i=1;i<=n;++i){s[i]={read(),read(),read()};b[++tot]=s[i].l;b[++tot]=++s[i].r;}sort(b+1,b+tot+1);tot=unique(b+1,b+tot+1)-b-1;for(int i=1;i<=n;++i){s[i].l=lower_bound(b+1,b+tot+1,s[i].l)-b;s[i].r=lower_bound(b+1,b+tot+1,s[i].r)-b;c[++tt]=make_pair(s[i].l,i);c[++tt]=make_pair(s[i].r,-i);}sort(c+1,c+tt+1);multiset<pair<int,int>>st;for(int i=1,j;i<=tt;++i){j=c[i].second;if(j>0){auto it=st.emplace(s[j].a,j);if(it!=st.begin()){int k=prev(it)->second;e[++fuc]={j,k,abs(s[j].a-s[k].a)};}if(next(it)!=st.end()){int k=next(it)->second;e[++fuc]={j,k,abs(s[j].a-s[k].a)};}}else st.erase(make_pair(s[-j].a,-j));}kruskal();}return fw,0;
}

T6 [JOISC 2017 Day 2] 火车旅行

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/821394.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

免费的erp系统有哪些

在市场上,有一些免费的ERP系统,它们提供了成本效益高、功能齐全的解决方案。这些系统包括:1. Odoo;2.Dolibarr;3.ERPNext;4.xTuple PostBooks;5.FrontAccounting;6.Apache OFBiz;7.Metasfresh。Odoo是一个全面的开源ERP解决方案,适用于中小型企业,尤其是那些需要高度…

扩展KMP

前言 扩展KMP又称Z函数,可以快速的求出一个字符串的每一个后缀的与其的LCP(最大公共前缀)长度。 至于为什么要学习exKMP,因为(数据规模很上进)我们都是上进的OIer。 算法思路 暴力朴素的算法 将\(n\)个字符的字符串S中第\(i\)位开始的后缀与S的开头一一比较,求出LCP数组…

运算

模二加(异或)运算模 即取余 模运算 当两个整数 a 和 b 模 n 同余,记作 a≡b(mod n),意味着 a 和 b 被 n 除后余数相同。 基本性质:封闭性:如果 a≡b(mod n) 且 c≡d(mod n),那么 a+c≡b+d(mod n),以及 a⋅c≡b⋅d(mod n)。 乘法逆元:对于任何整数 a 和正整数 n,存在一…

Knapsack题解

题面下划线:符合单调性 举例:k=3W : 1 2 3 4 5V : 6 5 1 2 4g: 15 11 7 6 4

Win11安装基于WSL2的Ubuntu

详细介绍了Win11安装基于WSL2的Ubuntu的过程。1. 概述 趁着还没有完全忘记,详细记录一下在Win11下安装基于WSL2的Ubuntu的详细过程。不得不说WLS2现在被微软开发的比较强大了,还是很值得安装和使用的,笔者就通过WLS2安装的Ubuntu成功搭建了ROS环境。 2. 详论 2.1 子系统安装…

什么是芯片领域的敏捷设计(Agile Development)

芯片领域的敏捷设计是一种灵活的开发方法,致力于更快速、更高效地开发和优化集成电路(IC)和半导体技术。其核心特点包括:1、迭代开发;2、跨功能团队合作;3、客户反馈导向;4、及时响应变更。其中,迭代开发强调分阶段、小步快跑的设计方法,有助于快速地调整和优化设计。…

Clickhouse基本使用方法详细讲解(包括详细步骤及相关操作截图)

超详细ClickHouse学习笔记 一、ClickHouse概述 ClickHouse是一个用于在线分析处理查询(OLAP)的列式数据库管理系统(DBMS)。它由Altinity公司开发,支持线性扩展和高性能的数据压缩。ClickHouse以其卓越的数据处理速度而闻名,特别适合于大规模数据集的实时查询和分析。OLTP…

高ROI的行业或项目一般有什么样的特点

高ROI的行业或项目一般的特点有:1、市场需求的洞察;2、有效的成本控制;3、创新和技术应用;4、强大的市场定位;5、风险管理。实现高ROI的项目通常基于对市场需求的深刻洞察。这意味着项目管理者或企业家需要了解消费者的需求,预测市场趋势,并能够提供满足这些需求的产品或…

IDEA如何更改背景桌面图片

前言 我们在使用IDEA开发Java应用时,经常对着屏幕,有时候也感觉有点枯燥,这时候,就可以设置一个背景图片来缓解一下,比如说设置一张风景图片,或者设置一个美女图片,哈哈哈。 那么,我们可以如何设置呢? 如何设置 首先,我们打开设置面板。然后,我们点击下【Appearance…

灰色代码部分:要是输入名字列表,又能输出结果,但是空列表的时候就输出不了?

大家好,我是Python进阶者。 一、前言 前几天在Python白银交流群【Aciel】问了一个Python基础的问题,问题如下:灰色代码部分:要是输入名字列表,又能输出结果,但是空列表的时候就输出不了?二、实现过程 这里【瑜亮老师】给了一个指导,具体如下所示:@Aciel 循环 for nam…

assembly2

汇编2 寄存器(不同架构不同) 8086中寄存器均为16位,可存放两个字节(1byte=8bit)。 通用寄存器AX,BX,CX,DX用来存储一般性的数据,被称为通用寄存器。二进制数据在寄存器中是低位存在低地址,高位存在高地址。也可将一个寄存器分为H,L(高8位低8位)来做8位存储器。字在寄存器…

shctf[Week3] 小小cms

看到首页面 了解到YzmCMS的版本,上网搜搜这个版本存在的漏洞 发现存在任意函数调用RCE的漏洞 https://blog.csdn.net/shelter1234567/article/details/138524342 根据文章直接抄payload,进入/pay/index/pay_callback目录下下面cat /flag即可,最终的payload为 out_trade_no[0…