Noip前的大抱佛脚----图论
图论
Tags:Noip前的大抱佛脚
知识点
二分图相关
DFS找环
From [CodeForces19E] Fairy
环一定是DFS树上的返祖边
通过对偶环+1,奇环-1可以找到出现在所有奇环上的一条边
这种方法也可以求图中最多的不相交的圆的个数
并查集维护二分图
在数据结构有讲,用按秩合并的带权并查集维护黑白颜色即可
二分图匹配的不可行边
求一定不会出现在二分图最大匹配中的边
方法是:跑网络流,对残余网络跑\(Tarjan\)缩点,一条边不是不可行边当且仅当它是匹配边或者他们被缩在同一个点中
感性理解:缩在同一点,把环给反向,于是就可以变成匹配边了
注意:Dinic跑二分图复杂度为\(O(\sqrt n m)\)!!
最小生成树相关
最短路树
要求生成树中每个点到1号点的距离等于图中最短路,且生成树边权之和最小
这个直接用SPFA或者Dijkstra求从哪里来是最短路,然后贪心选个最短的就好了
最短路相关
负环
可以使用SPFA,先同时把所有点入队,然后判断一个点的最短路如果经过超过\(n\)个点,则表示存在负环
多源最短路
把源点同时入队/入堆,然后照样跑即可,同时如果需要可以记一个from表示从哪个源点过来的
如果不需要记录,新建源点向所有源点连0的边即可
差分约束系统
连边以及跑最长/短路
对于一些形如 \(v_a\le v_b+W\) 的若干关于"\(\le\)“的式子,可以从从\(b\)向\(a\)连一条权值为\(W\)的边,然后跑最短路
最后跑出来的答案\(dis[a]\)表示的是关于\(v_a\)的若干不等式的解,就是说不等式组中最小的那个,所以\(dis[a]\)是\(v_a\)的最大值
如果是要求最小值,那么对于上面的式子,可以从\(a\)向\(b\)连\(-W\)的边,跑最长路,得出的\(dis[b]\)是\(b\)的最小值
无解以及做题方法
可以通过存在正/负环判断无解
很多时候模型并不明确,可以二分答案之后判断合法性来求解一般问题
01最短路
边权只有0或1的最短路,感觉是Dijkstra+SPFA,操作是开一个双端队列deque,每次更新最短路,如果由+0更新就丢队头,+1更新就丢队尾,然后每个点只往外面更新一次,其实是用队列模拟Dij的堆,这样复杂度是\(O(m)\)的
k短路
求一张有向图中\(1\)到\(n\)中第\(k\)短的路径,可以不是简单路径(有环,自环,重边)
做法1:A*搜索
建出以\(n\)为根的最短路树,那么每个点有两个参数:\(f[i]\)表示到达\(i\)点已经走了\(f[i]\)的长度,\(h[i]\)表示\(i\)的估价函数,表示\(i\)到\(n\)最少还要走多远,以这个为权值每次在堆中选取权值最小的更新,\(n\)被走到的第\(k\)次即为\(k\)短路
做法2:可持久化左偏树
建出最短路树后我们发现对于任意一条路径,可以拆成树边和非树边,且一个非树边的边集对应着一条路径,于是我们需要求出第\(k\)小的非树边集
令\(dis[i]\)表示\(i\)到\(n\)的最短路长度,则定义一个非树边集\(S\)的权值为\(dis[1]+\sum_{e\in S}d[e],d[e]=dis[to]+e.w-dis[fr]\),\(d[e]\)的实际含义是走这条边新增的代价,于是把这个丢进优先队列里取出\(k\)次就好了
然后我们考虑怎么得到非树边集&怎么拓展新状态。
对于每个点,维护该点到树根(n)的路径的所有非树边的出边集,并按照出边的\(d[e]\)排序(这个用可持久化左偏树得到,每次从父亲那里copy下来,代码见魔法猪学院)
定义一个全局的优先队列按照\(bs+val\)排序,\(bs\)表示已经走了的边的权值,\(val\)表示当前边集的权值。首先把1号点的左偏树树顶丢入全局堆中,然后从堆中取出k次元素得到第k小
对于一个状态是当前最小,由此可以拓展出三个新状态:该点的左偏树中的左右儿子(表示为从该点向上不选择原来的非树边,转而选择一条比它大一点的非树边。需要堆中父亲一定大于左右儿子的性质做保证)、该条边指向的点的堆顶(表示为一定选这条边,在往n走的路径中再选一条非树边,此时要给\(bs_{new}+=bs_{old}+val_{old}\))
复杂度:\(O(nlogn+mlogm+klogk)\),分别为最短路、左偏树、优先队列的复杂度之和
网络流
zkw费用流
用SPFA跑出最短路之后,用类似Dinic的dfs跑很多遍,对于相同费用的路径较多的图的效率特别高,当然不满足这种性质效率就会比一般的费用流效率低(无限之环加上zkw费用流快了100倍!)
int dfs(int x,int flow)
{
if(x==T) return flow;vis[x]=1;
for(int &i=cur[x];i;i=a[i].next)
{
int R=a[i].to;//dis表示的费用最短路
if(!a[i].w||dis[R]!=dis[x]+a[i].cost||vis[R]) continue;
int k=dfs(R,min(flow,a[i].w));
if(k) {a[i].w-=k,a[i^1].w+=k;vis[x]=0;return k;}
}
return 0;//如果在这个点找不到增广路,vis就只会在SPFA中清空,这个点在本次dis数组时不会再访问
}
while(SPFA())
{
for(int i=1;i<=T;i++) cur[i]=head[i];
while(int tmp=dfs(S,inf)) mxfl-=2*tmp,ans+=tmp*dis[T];
}
做题经验
同余类最短路
来源:墨墨的等式
求\(a_1x_1+a_2x_2+...+a_nx_n=B\),其中\(x\)都有非负整数解,求\(B\)在一定范围内的取值个数
巧妙地把每个解归类为\(\%a_1\)的余数那个点,对每个点往外连边跑最短路,表示得到\(\%a_1=k\)的最小的\(B\)是多大,从而推出\(B\)的个数
边权是max的形式
代价是进入该点和从该点出去的边权\(max\),求\(1\)到\(n\)的最小代价
注意题目是无向图,所以可以每条边拆成两个,然后左右对应,差分建图
其实遇到max都应该要想想差分
图论模型的转换
遇到以下情况可以考虑图论模型来连边
- 有两类点,两类点之间有关联,然后可以考虑把A类点像B类点的两条边转为B类点的一条边(ZKJ太阳月亮匹配/棋盘上的守卫)
- 两个点相互关联,知道其中一个就可以推导另一个
- 一个物品有两个权值,权值值域一定(ZKJ烟火左右权可翻转,求最大权连续序列)
边定向
这是一种思考方向,可以参考[BZOJ4883]棋盘上的守卫
树上点覆盖
一个关键点能够覆盖距离它不超过k的点,要求覆盖所有点的最小关键点数
这题是贪心不是DP啊QAQ
两树叠图的最小割及方案数
答案为2或者3,所以一定是A树断一边,B树断若干边构成的
把A作为树形结构,B树的每条边\(tag[x]++,tag[y]++,tag[lca(x,y)]-=2\)
所以子树之和便是子树内向外面连的边数之和
一类BFS最小生成树做法
给定网格图/树结构,求一些关键点的最小生成树(10.15YLT3)
方式是以关键点为源跑多源最短路,一个点被经过的第二次/一条边连接的两点的最近源点不同,就可以连接这两个源点了。注意边数还是原来的边数级别的
图论模板库
Tarjan相关
强连通分量:有向图中任意两个顶点都有相互到达的路径的一个极大子图
边双连通分量:一个子图中删去任意一条边都不影响图的连通性
点双连通分量:一个子图中删去任意一个点都不影响图的连通性
割边:连接两个边双的边
割点:连接两个点双的点
割边
//把一个边双缩点
void Tarjan(int x)
{
vis[x]=1;sta[++top]=x;
dfn[x]=low[x]=++tot;
for(int i=head[x];i;i=a[i].next)
{
int R=a[i].to;
if(!dfn[R]) Tarjan(R),low[x]=min(low[x],low[R]);
else if(vis[R]) low[x]=min(low[x],low[R]);
}
if(low[x]!=dfn[x]) return;
for(int k=sta[top],lst=0;lst!=x;lst=k,k=sta[--top])
vis[k]=0,bel[k]=x;
}
//无向图缩点略有不同
割点
//求割点(tag[x]=1)
void Tarjan(int x,int f)
{
int s=0;dfn[x]=low[x]=++tot;
for(int i=head[x];i;i=a[i].next)
{
int R=a[i].to;if(R==f) continue;
if(!dfn[R])
{
s++;Tarjan(R,x);tag[x]|=low[R]>=dfn[x];
low[x]=min(low[x],low[R]);
}
else low[x]=min(low[x],dfn[R]);//注意!这里必须是dfn,有反例!!
}
if(!f&&s==1) tag[x]=0;
}
有向图缩点后成为DAG,无向图缩点/求割点后成为树的结构
圆方树
int n,m,dfn[N],low[N],sta[N],top,node,tot,siz[N];
void Min(int &a,int b) {if(b<a) a=b;}
void Tarjan(int x)
{
dfn[x]=low[x]=++tot;sta[++top]=x;siz[x]=-1;
for(int i=A.head[x];i;i=A.a[i].next)
{
int R=A.a[i].to;
if(dfn[R]) {Min(low[x],dfn[R]);continue;}
Tarjan(R);Min(low[x],low[R]);
if(low[R]>=dfn[x])
{
B.link(++node,x);siz[node]=1;
for(int k=sta[top],lst=0;lst!=x;lst=k,k=sta[--top])
B.link(node,k),siz[node]++;
}
}
//node初值为n
}
2-SAT
具体作用。。。不可描述。。。
大概就是给出每个点选或不选使得满足所有条件吧。
输出方案的话选择超级点编号小的那个选择输出
struct edge{int next,to;}a[N];
int n,m,low[N],dfn[N],tot,sta[N],top,bel[N],node,in[N],head[N],cnt;
int rev(int x) {return x>n?x-n:x+n;}
void Min(int &a,int b) {if(b<a) a=b;}
void link(int x,int y) {a[++cnt]=(edge){head[x],y};head[x]=cnt;}
void Tarjan(int x)
{
dfn[x]=low[x]=++tot;sta[++top]=x;in[x]=1;
for(int i=head[x];i;i=a[i].next)
{
int R=a[i].to;
if(!dfn[R]) Tarjan(R),Min(low[x],low[R]);
else if(in[R]) Min(low[x],low[R]);
}
if(low[x]!=dfn[x]) return;node++;
for(int lst=0,k=sta[top];lst!=x;lst=k,k=sta[--top])
bel[k]=node,in[k]=0;
}
int main()
{
cin>>n>>m;
for(int i=1,x,a,y,b;i<=m;i++)
{
scanf("%d%d%d%d",&x,&a,&y,&b);
x+=a*n;y+=b*n;
link(rev(x),y);link(rev(y),x);
}
for(int i=1;i<=n*2;i++) if(!dfn[i]) Tarjan(i);
for(int i=1;i<=n;i++)
if(bel[i]==bel[rev(i)]) return puts("IMPOSSIBLE"),0;
puts("POSSIBLE");
for(int i=1;i<=n;i++)
printf("%d ",bel[i]<bel[rev(i)]?0:1);
}
TarjanLCA
\(O(n*反阿克曼函数+Q)\)离线求lca
步骤:
- 对询问挂链(vector),初始化并查集
- \(dfs\)整棵树后处理询问,扫完该子树才将其并查集父亲指向树结构的父亲
- 如果对于一个询问\((x,y)\),当前扫到\(y\)且\(x\)已经被访问过,答案为x的并查集父亲
void tarjan(int x,int fr)
{
for(int i=head[x];i;i=a[i].next)
if(R!=fr) tarjan(R,x);vis[x]=1;
for(int i:U[x])
{
int y=g[i]^x,&s=ans[i];//g[i]=x^y,表示第i个询问
if(vis[y]) s=find(y);
}
fa[x]=fa[fr];
}
最短路相关
SPFA判负环
方式是先把所有点入队了,然后若有一个点的最短路超过n个点即存在负环
复杂度\(O(n^2)\)
注意判自环!
for(int i=1;i<=n;i++) Q.push(i),vis[i]=1;
while(!Q.empty())
{
int x=Q.front();
for(int i=head[x];i;i=a[i].next)
{
int R=a[i].to;
if(dis[R]<=dis[x]+a[i].w) continue;
dis[R]=dis[x]+a[i].w;
f[R]=f[x]+1;
if(f[R]==n+1) return 1;
if(!vis[R]) Q.push(R),vis[R]=1;
}
Q.pop();vis[x]=0;
}
Dijkstra
稳定的\(O(nlogn)\)单源最短路做法
struct Node
{
int x;ll dis;
int operator < (const Node&A)const
{return dis>A.dis;}
};
int n,m,vis[N],head[N],cnt,S;ll dis[N];
priority_queue<Node> Q;
void Dijkstra()
{
memset(dis,127,sizeof(dis));
Q.push((Node){S,0});dis[S]=0;
while(!Q.empty())
{
int x=Q.top().x;Q.pop();
if(vis[x]) continue;vis[x]=1;
for(int i=head[x];i;i=a[i].next)
{
int R=a[i].to;
if(dis[R]<=dis[x]+a[i].w) continue;
dis[R]=dis[x]+a[i].w;
Q.push((Node){R,dis[R]});
}
}
}
网络流
最大流
\(Dinic\)算法\(O(n^3)\),二分图\(O(m\sqrt n)\)
const int N=1e4+10,inf=1e9;
struct edge{int next,to,w;}a[N*21];
int n,m,S,T,dep[N],head[N],cnt=1,cur[N],ans;
queue<int> Q;
void link(int x,int y,int w)
{
a[++cnt]=(edge){head[x],y,w};head[x]=cnt;
a[++cnt]=(edge){head[y],x,0};head[y]=cnt;
}
int BFS()
{
memset(dep,0,sizeof(dep));
Q.push(S);dep[S]=1;
while(!Q.empty())
{
int x=Q.front();Q.pop();
for(int i=head[x];i;i=a[i].next)
if(!dep[a[i].to]&&a[i].w) dep[a[i].to]=dep[x]+1,Q.push(a[i].to);
}
return dep[T];
}
int DFS(int x,int flow)
{
if(x==T) return flow;
for(int &i=cur[x];i;i=a[i].next)
{
int R=a[i].to;
if(!a[i].w||dep[R]!=dep[x]+1) continue;
int k=DFS(R,min(flow,a[i].w));
if(k) {a[i].w-=k;a[i^1].w+=k;return k;}
}
return 0;
}
int main()
{
cin>>n>>m>>S>>T;
for(int i=1,x,y,w;i<=m;i++)
{
scanf("%d%d%d",&x,&y,&w);
link(x,y,w);
}
while(BFS())
{
for(int i=1;i<=n;i++) cur[i]=head[i];
while(int tmp=DFS(S,inf)) ans+=tmp;
}
cout<<ans<<endl;
return 0;
}
费用流
struct edge{int next,to,w,c;}a[N*20];
int n,m,S,T,head[N],cnt=1,dis[N],pe[N],px[N],vis[N];
ll ans,f;queue<int> Q;
void link(int x,int y,int w,int c)
{
a[++cnt]=(edge){head[x],y,w,c};head[x]=cnt;
a[++cnt]=(edge){head[y],x,0,-c};head[y]=cnt;
}
int SPFA()
{
memset(dis,63,sizeof(dis));
Q.push(S);dis[S]=0;vis[S]=1;
while(!Q.empty())
{
int x=Q.front();
for(int i=head[x];i;i=a[i].next)
{
int R=a[i].to;
if(dis[R]<=dis[x]+a[i].c||!a[i].w) continue;
dis[R]=dis[x]+a[i].c;
pe[R]=i;px[R]=x;
if(!vis[R]) vis[R]=1,Q.push(R);
}
Q.pop();vis[x]=0;
}
return dis[T]!=dis[0];
}
int main()
{
cin>>n>>m>>S>>T;
for(int i=1,x,y,w,f;i<=m;i++)
{
scanf("%d%d%d%d",&x,&y,&w,&f);
link(x,y,w,f);
}
while(SPFA())
{
int flow=1e9;
for(int i=T;i!=S;i=px[i]) flow=min(flow,a[pe[i]].w);
for(int i=T;i!=S;i=px[i]) a[pe[i]].w-=flow,a[pe[i]^1].w+=flow;
f+=flow,ans+=flow*dis[T];
}
cout<<f<<" "<<ans<<endl;
}
Noip前的大抱佛脚----图论的更多相关文章
- Noip前的大抱佛脚----文章索引
Noip前的大抱佛脚----赛前任务 Noip前的大抱佛脚----考场配置 Noip前的大抱佛脚----数论 Noip前的大抱佛脚----图论 Noip前的大抱佛脚----动态规划 Noip前的大抱佛 ...
- Noip前的大抱佛脚----Noip真题复习
Noip前的大抱佛脚----Noip真题复习 Tags: Noip前的大抱佛脚 Noip2010 题目不难,但是三个半小时的话要写四道题还是需要码力,不过按照现在的实力应该不出意外可以AK的. 机器翻 ...
- Noip前的大抱佛脚----字符串
目录 字符串 经验 用FFT求解字符串匹配问题 两(多)串DP时状态合并 最长公共子序列转LIS 位运算最大值 挂链哈希 哈希处理回文串 树哈希 字符串模板库 KMP 最小循环表示 Mancher A ...
- Noip前的大抱佛脚----一些思路
目录 一些思路 序列 函数问题 网格图 删除和询问 乘法问题 顺序问题 最值问题 研究成果 数论分块套数论分块的复杂度 一些思路 Tags:Noip前的大抱佛脚 序列 线段树(当然还要有主席树啊!) ...
- Noip前的大抱佛脚----数论
目录 数论 知识点 Exgcd 逆元 gcd 欧拉函数\(\varphi(x)\) CRT&EXCRT BSGS&EXBSGS FFT/NTT/MTT/FWT 组合公式 斯特林数 卡塔 ...
- Noip前的大抱佛脚----数据结构
目录 数据结构 知识点及其应用 线段树 神奇标记 标记不下放 并查集 维护二分图 维护后继位置 堆 可并堆的可持久化 dsu on tree 方式&原理 适用范围 单调队列 尺取合法区间 模板 ...
- Noip前的大抱佛脚----赛前任务
赛前任务 tags:任务清单 前言 现在xzy太弱了,而且他最近越来越弱了,天天被爆踩,天天被爆踩 题单不会在作业部落发布,所以可(yi)能(ding)会不及时更新 省选前的练习莫名其妙地成为了Noi ...
- Noip前的大抱佛脚----根号对数算法
根号算法 分块 数列分块入门九题(hzwer) 入门题1,2,3,4,5,7 问题:给一段区间打上标记后单点查询 解法:主要是每块维护一些标记,计算答案等,此类分块较为简单 注意:块大小一般为\(\s ...
- Noip前的大抱佛脚----奇技淫巧
STL函数 set set查找前驱后继 multiset<int>::iterator iter; S.insert(x); iter=S.find(x);//返回迭代器 iter--;/ ...
随机推荐
- leetCode题解之寻找一个数在有序数组中的范围Search for a Range
1.问题描述 Given an array of integers sorted in ascending order, find the starting and ending position o ...
- GoldenGate搭建与运维
GolenGate介绍 GoldenGate软件是一种基于日志的结构化数据复制软件,它通过解析源数据库在线日志或归档日志获得数据的增删改变化,再将这些变化应用到目标数据库,实现源数据库与目标数据库实时 ...
- Oracle EBS 报表日期格式问题
1.确保参数日期值集选择:FND_STANDARD_DATE 2.将程序的入口参数选择为 varchar2类型 3.注意输出和游标时参数的截断 to_date(substr(p_DATE_from, ...
- [WinCE] [Win10] Win10 Creator 升级后 Windows Mobile Device Center 不能打开
运行 services.msc 找到 Windows Mobile 2003-based device connectivity服务,右键属性,Log On选项卡选择 Local System acc ...
- asp.net MVC 使用PagedList.MVC实现分页
在上一篇的EF之DB First中,存在以下的两个问题: 1. 添加/编辑页面显示的是属性名称,而非自定义的名称(如:姓名.专业...) 2. 添加/编辑时没有加入验证 另外数据展示使用分页 @Htm ...
- spring mvc 解决跨域问题
Spring MVC 从4.2版本开始增加了对CORS的支持. 在Controller上使用@CrossOrigin注解: // 指定域名 @CrossOrigin("http://doma ...
- 使用WebViewJavascriptBridge与UIWebView交互
使用WebViewJavascriptBridge与UIWebView交互 https://github.com/marcuswestin/WebViewJavascriptBridge 核心的地方: ...
- swift中变量的几种类型
swift中变量的几种类型 swift中变量分为 optional,non-optional 以及 implicitly unwrapped optional 这几种类型 var nullablePr ...
- jumpserver 问题,给自己看的哦,你们不准看哟
给自己看的,排版忽略! http://docs.jumpserver.org/zh/docs/setup_by_centos7.html 看完全部文档后解决不了再看下边的!!! docker 服务启动 ...
- SQL Server中搜索特定的对象
一.注释中带某关键字的对象 主要用到 sys.tables .sys.columns .sys.procedures 系统对象表以及sys.extended_properties 扩展属性表 --查 ...