LCA&最小生成树
LCA 经常被用来使用。比如询问树两点之间的距离。
比如树上差分 都是经常被使用的类型。有的时候倍增求LCA的同时还可以优化算法。
这道题呢 求一个严格的最小生成树,当然如果不严格的话如果有重边那么就和原来一样喽。
这里推广一些关于最小生成树的定理 在图中 最小生成树边权一定是一定的。
那么由此可以推出不同的最小生成树也就是边不同但是他们被连在图中的边权一定相等。
所以上述结论和这道题没什么关系 关键是严格的次小生成树 。
我们可以加边 来完成这整个操作,考虑到对于每一条边造成的影响还是这两点之间的最短距离的影响。仔细想就知道了。
那么影响转化成了他们到LCA这些边的影响,LCA之外的边可就没什么事了。
我们只需要求出他们到LCA的最长边有多大即可。如果相等了那么没用了么?此时必须要维护一个次小的边权已被不时只需。
建模成功! 我们把问题成功转换成了 求两点之间到LCA之间的最大边权 严格次大边权。
LCA 可以tarjan,复杂度非常优秀可是考虑到 一些问题,求不出边权 先求出祖先再往上爬不就好了?
这样写有些 复杂度nm 并不能通过此题。考虑倍增求出LCA
在往上跳跃的时候我们维护一个最大值边权和次大值边权即可。复杂度mlgn
//#include<bits/stdc++.h>
#include<iostream>
#include<cstdio>
#include<iomanip>
#include<cstring>
#include<string>
#include<cstdlib>
#include<cmath>
#include<algorithm>
#include<cctype>
#include<utility>
#include<set>
#include<bitset>
#include<queue>
#include<stack>
#include<deque>
#include<map>
#include<vector>
#include<ctime>
#define ll long long
#define INF 2147483646
#define z(i) t[i].z
using namespace std;
char buf[<<],*fs,*ft;
inline char getc()
{
return (fs==ft&&(ft=(fs=buf)+fread(buf,,<<,stdin),fs==ft))?:*fs++;
}
inline ll read()
{
ll x=,f=;char ch=getc();
while(ch<''||ch>''){if(ch=='-')f=-;ch=getc();}
while(ch>=''&&ch<=''){x=x*+ch-'';ch=getc();}
return x*f;
}
inline void put(ll x)
{
x<?putchar('-'),x=-x:;
ll num=;char ch[];
while(x)ch[++num]=x%+'',x/=;
num==?putchar(''):;
while(num)putchar(ch[num--]);
putchar(' ');return;
}
const ll MAXN=,maxn=;
ll n,m,sum,cnt,T,maxx,maxx1,minn;
ll lin[MAXN<<],nex[MAXN<<],ver[MAXN<<],e[MAXN<<],len=;
ll clin[MAXN<<],cnex[MAXN<<],cver[MAXN<<],ce[MAXN<<],clen=;
ll dis[maxn],depth[maxn],vis[maxn],f[maxn][],g[maxn][][];//f[i][k]表示i节点向上爬2^k个节点
//g[i][k][0/1]表示i节点向上爬2^k个节点之中的最大值 严格次大值
priority_queue<pair<ll,pair<ll,ll> > > q;
inline ll max(ll x,ll y){return x>y?x:y;}
inline ll min(ll x,ll y){return x>y?y:x;}
inline void swap(ll &x,ll &y){ll t;t=x;x=y;y=t;}
struct wy{ll x,y,z;}t[MAXN];
void add(ll x,ll y,ll z)
{
ver[++len]=y;
nex[len]=lin[x];
lin[x]=len;
e[len]=z;
}
void cadd(ll x,ll y,ll z)
{
cver[++clen]=y;
cnex[clen]=clin[x];
clin[x]=clen;
ce[clen]=z;
}
void prim()
{
for(ll i=;i<=n;i++)dis[i]=INF;
memset(vis,,sizeof(vis));
dis[]=;ll flag=;
q.push(make_pair(dis[],make_pair(,)));
while(q.size())
{
ll te=q.top().second.first;
ll yy=q.top().second.second;
q.pop();
if(vis[te]==)continue;
vis[te]=;sum+=dis[te];
++flag;if(flag!=)cadd(te,yy,dis[te]),cadd(yy,te,dis[te]);
for(ll i=lin[te];i;i=nex[i])
{
ll tn=ver[i];
if(vis[tn]==)continue;
if(dis[tn]>e[i])
{
dis[tn]=e[i];
q.push(make_pair(-dis[tn],make_pair(tn,te)));
}
}
}
}
void dfs(ll x,ll fa)
{
depth[x]=depth[fa]+;
f[x][]=fa;
for(ll i=;i<=T;++i)
{
f[x][i]=f[f[x][i-]][i-];
g[x][i][]=max(g[x][i-][],g[f[x][i-]][i-][]);
if(g[x][i-][]==g[f[x][i-]][i-][])g[x][i][]=max(g[x][i-][],g[f[x][i-]][i-][]);
if(g[x][i-][]>g[f[x][i-]][i-][])g[x][i][]=max(g[x][i-][],g[f[x][i-]][i-][]);
if(g[x][i-][]<g[f[x][i-]][i-][])g[x][i][]=max(g[x][i-][],g[f[x][i-]][i-][]);
}
for(ll i=clin[x];i;i=cnex[i])
{
ll tn=cver[i];
if(tn==fa)continue;
g[tn][][]=ce[i];
g[tn][][]=-INF;
dfs(tn,x);
}
}
void lca(ll x,ll y)
{
if(depth[x]>depth[y])swap(x,y);//y>x
for(ll i=T;i>=;--i)
{
if(depth[f[y][i]]>=depth[x])
{
if(maxx>g[y][i][])maxx1=max(maxx1,g[y][i][]);
if(maxx<g[y][i][])maxx1=max(maxx,maxx1);
maxx=max(maxx,g[y][i][]);
maxx1=max(maxx1,g[y][i][]);
y=f[y][i];
}
}
if(x==y)return;
for(ll i=T;i>=;--i)
{
if(f[x][i]!=f[y][i])
{
if(maxx>g[y][i][])maxx1=max(maxx1,g[y][i][]);
if(maxx<g[y][i][])maxx1=max(maxx,maxx1);
if(maxx>g[x][i][])maxx1=max(maxx1,g[x][i][]);
if(maxx<g[x][i][])maxx1=max(maxx,maxx1);
maxx=max(maxx,g[x][i][]);
maxx1=max(maxx1,g[x][i][]);
maxx=max(maxx,g[y][i][]);
maxx1=max(maxx1,g[y][i][]);
x=f[x][i];y=f[y][i];
}
}
if(maxx>g[y][][])maxx1=max(maxx1,g[y][][]);
if(maxx<g[y][][])maxx1=max(maxx,maxx1);
if(maxx>g[x][][])maxx1=max(maxx1,g[x][][]);
if(maxx<g[x][][])maxx1=max(maxx,maxx1);
maxx=max(maxx,g[x][][]);
maxx=max(maxx,g[y][][]);
return;
}
int main()
{
//freopen("1.in","r",stdin);
n=read();m=read();
for(ll i=;i<=m;++i)
{
ll x,y,z;
x=read();y=read();z=read();
t[i].x=x;t[i].y=y;z(i)=z;
add(x,y,z);add(y,x,z);
}
prim();
//put(sum);
T=(ll)(log(n*1.0)/log(*1.0))+;
//put(T);
dfs(,);minn=INF;
for(ll i=;i<=m;++i)
{
maxx=maxx1=-INF;
lca(t[i].x,t[i].y);
if(maxx==z(i))minn=min(minn,z(i)-maxx1);
else minn=min(minn,z(i)-maxx);
}
put(sum+minn);
return ;
}
这道题呢 很有意思但是我并没有观察到问题的特异性 。
可能是 写题的时候大脑太累了 不思考了,以后累的时候一定要休息一会效率不但高而且很敏锐的就可以破解问题。
注意 最最小生成树的个数 每个最小生成树相同边权的个数一定是相同的,因为考虑如果有一个更优的边权可以加到图中,我的最小生成树还是最小生成树么,
那如果是刚好有一条边让出来位置呢,那么我的最小生成树边权的相同边权的个数还是一定的。证毕。
那么 根据前面一道题我的废话 就可以得到一个算法了 前面的废话是一些点 被连在最小生成树上的边权一定是相等的。
也就是联通块每次都是一样的只不过点连到联通块的方式不太一样。那么既然n只有100 也就是边至多99条。
且每条边权值只有10条 那么 爆搜在最小生成树上的每一条边即可。
复杂度 2^n*n 多么完美啊,加上玄学剪枝 直接完爆正解!!!
//#include<bits/stdc++.h>
#include<iostream>
#include<cstdio>
#include<iomanip>
#include<cstring>
#include<string>
#include<cstdlib>
#include<cmath>
#include<algorithm>
#include<cctype>
#include<utility>
#include<set>
#include<bitset>
#include<queue>
#include<stack>
#include<deque>
#include<map>
#include<vector>
#include<ctime>
#define ll long long
#define INF 2147483647
#define z(i) t[i].z
#define x(i) t[i].x
#define y(i) t[i].y
#define mod 31011
using namespace std;
char buf[<<],*fs,*ft;
inline char getc()
{
return (fs==ft&&(ft=(fs=buf)+fread(buf,,<<,stdin),fs==ft))?:*fs++;
}
inline int read()
{
int x=,f=;char ch=getc();
while(ch<''||ch>''){if(ch=='-')f=-;ch=getc();}
while(ch>=''&&ch<=''){x=x*+ch-'';ch=getc();}
return x*f;
}
inline void put(int x)
{
x<?putchar('-'),x=-x:;
int num=;char ch[];
while(x)ch[++num]=x%+'',x/=;
num==?putchar(''):;
while(num)putchar(ch[num--]);
putchar(' ');return;
}
const int MAXN=;
int n,m,ans,cnt,sum=,w;
int f[MAXN];
struct wy
{
int x,y,z;
friend int operator <(const wy &p,const wy &u)
{
return p.z<u.z;
}
}t[MAXN];
struct wy1{int x,y,k,v;}b[MAXN];
int gf(int x){return x==f[x]?x:f[x]=gf(f[x]);}
int getfather(int x){return x==f[x]?x:getfather(f[x]);}
void dfs(int x,int depth,int s,int k)
{
if(s+depth-x+<k)return;
if(s==k){ans++;return;}
if(x==depth+)return;
dfs(x+,depth,s,k);
int xx=getfather(t[x].x);
int yy=getfather(t[x].y);
if(xx!=yy)
{
f[xx]=yy;
dfs(x+,depth,s+,k);
f[xx]=xx;f[yy]=yy;
}
return;
}
int main()
{
//freopen("1.in","r",stdin);
n=read();m=read();
for(int i=;i<=m;++i)
{
x(i)=read();
y(i)=read();
z(i)=read();
}
for(int i=;i<=n;++i)f[i]=i;
sort(t+,t++m);
for(int i=;i<=m;++i)
{
if(i==)b[++cnt].x=i;
else if(z(i)!=z(i-))b[cnt].y=i-,b[++cnt].x=i;
int xx=gf(x(i));
int yy=gf(y(i));
if(xx!=yy)
{
f[xx]=yy;ans+=z(i);
++b[cnt].k;++w;
}
}
b[cnt].y=m;
if(w!=n-){put();return ;}
//put(cnt);
//put(ans);
//for(int i=1;i<=cnt;++i)put(b[i].k);
for(int i=;i<=n;++i)f[i]=i;
for(int i=;i<=cnt;++i)
{
if(b[i].k)
{
ans=;
dfs(b[i].x,b[i].y,,b[i].k);
sum=(sum*(ans%mod))%mod;
for(int j=b[i].x;j<=b[i].y;++j)
{
int xx=gf(t[j].x);
int yy=gf(t[j].y);
f[xx]=yy;
}
}
}
put(sum);
return ;
}
LCA&最小生成树的更多相关文章
- BZOJ3732Network——kruskal重构树+倍增+LCA/最小生成树+倍增
题目描述 给你N个点的无向图 (1 <= N <= 15,000),记为:1…N. 图中有M条边 (1 <= M <= 30,000) ,第j条边的长度为: d_j ( 1 & ...
- uva11354 LCA+最小生成树+dp
源自大白书 题意 有n座城市通过m条双向道路相连,每条道路都有一个危险系数.你的任务是回答若干个询问,每个询问包含一个起点s和一个终点t,要求找到一条从s到t的路,使得途径所有的边的大最大危险系数最小 ...
- The Shortest Statement(Educational Codeforces Round 51 (Rated for Div.2)+最短路+LCA+最小生成树)
题目链接 传送门 题面 题意 给你一张有\(n\)个点\(m\)条边的联通图(其中\(m\leq n+20)\),\(q\)次查询,每次询问\(u\)与\(v\)之间的最短路. 思路 由于边数最多只比 ...
- 洛谷 P1967 货车运输 LCA + 最小生成树
两点之间边权最大值的最小值一定在图的最小生成树中取到. 求出最小生成树,进行倍增即可. Code: #include<cstdio> #include<algorithm> u ...
- 【Diary】
[写日记是好习惯] 前记 很随意的日记.想什么写什么,没有限制. 希望以后看到曾经,努力的自己比摸鱼的自己多. 2019.3 2019.3.29 第24次请假打卡 xzh:那些理科男以后都会当IT工作 ...
- bzoj3732: Network--kruskal最小生成树+LCA
这是一道写起来比较顺手的题目 没有各种奇怪的细节,基本就是Kruskal和倍增LCA的模板.. 题目大意:对于一个无向带权图,询问两点之间一条路,使得这条路上的最长边最小,输出最小最长边的的值 那么既 ...
- BZOJ3732 解析报告//LCA,最小生成树
3732: Network 题目描述 给你N个点的无向图 (1 <= N <= 15,000),记为:1…N. 图中有M条边 (1 <= M <= 30,000) ,第j条边的 ...
- Luogu 2245 星际导航(最小生成树,最近公共祖先LCA,并查集)
Luogu 2245 星际导航(最小生成树,最近公共祖先LCA,并查集) Description sideman做好了回到Gliese 星球的硬件准备,但是sideman的导航系统还没有完全设计好.为 ...
- 洛谷P4180 [Beijing2010组队]次小生成树Tree(最小生成树,LCT,主席树,倍增LCA,倍增,树链剖分)
洛谷题目传送门 %%%TPLY巨佬和ysner巨佬%%% 他们的题解 思路分析 具体思路都在各位巨佬的题解中.这题做法挺多的,我就不对每个都详细讲了,泛泛而谈吧. 大多数算法都要用kruskal把最小 ...
随机推荐
- linux每日命令(20):find命令概览
Linux下find命令在目录结构中搜索文件,并执行指定的操作.Linux下find命令提供了相当多的查找条件,功能很强大.由于find具有强大的功能,所以它的选项也很多,其中大部分选项都值得我们花时 ...
- python 的正则表达式
在python中,对正则表达式的支持是通过re模块来支持的.使用re的步骤是先把表达式字符串编译成pattern实例,然后在使用pattern去匹配文本获取结果. 其实也有另外一种方式,就是直接使用r ...
- 基于jQuery鼠标滚轮滑动到页面节点部分
基于jQuery鼠标滚轮滑动到页面节点部分.这是一款基于jQuery+CSS3实现的使用鼠标滚轮或者手势滑动到页面节点部分特效.效果图如下: 在线预览 源码下载 实现的代码. html代码: &l ...
- postman参数获取不到原因
在使用postman时,会发现经常提示参数错误,然而代码没有问题,仔细一看,原来是粘贴复制参数到postman时,前后有空格.
- linux中的信号机制
概述 Linux信号机制是在应用软件层次上对中断机制的一种模拟,信号提供了一种处理异步事件的方法,例如,终端用户输入中断键(ctrl+c),则会通过信号机制停止一个程序[1]. 这其实就是向那个程序( ...
- Java知多少(70)面向字节流的应用
文件输入输出流 文件输入输出流 FileInputStream 和 FileOutputStream 负责完成对本地磁盘文件的顺序输入输出操作. [例 10-5]通过程序创建一个文件,从键盘输入字符, ...
- spring mvc中获取请求URL
String baseUrl=request.getScheme()+"://"+request.getServerName()+":"+request.get ...
- 在windows下安装git后没有ssh文件夹
在windows7下安装git后,运行 cd ~/.ssh $ bash: cd: /c/Users/Administrator/.ssh: No such file or directory 出现以 ...
- Ubuntu下Apache虚拟主机+反向代理
反向代理 就是通过一台代理服务器,让Internet用户可以访问到内部网络上的服务器 下图中192.168.0.4 可以理解带有2个网卡,一个是公网ip,一个是192.168.0.4 代理内外中的2个 ...
- yum 快速安装centos7 mysql5.7
CentOS7 yum方式安装MySQL5.7 在CentOS中默认安装有MariaDB,这个是MySQL的分支,但为了需要,还是要在系统中安装MySQL,而且安装完成之后可以直接覆盖掉Maria ...