[SCOI2005]繁忙的都市
Link
这是一道模板题,不细讲。但是这涉及到最小生成树一个重要性质:
u
u
u到
v
v
v的路径中,最大值最小的一条是最小生成树上的一条路径。
#include<bits/stdc++.h>
#define ll long long
#define N 305
#define inf 0x3f3f3f3f
#define pb push_back
#define mp make_pair
#define lowbit(i) ((i)&(-i))
using namespace std;
int n,m,fa[N],cnt,ans;
struct node{
int u,v,w;
bool operator <(const node &o) const{
return o.w > w;
}
}e[N*N];
int find(int x){
return fa[x]==x?fa[x]:fa[x] = find(fa[x]);
}
void uni(int x,int y){
fa[find(x)] = find(y);
}
int main(){
//freopen(".in","r",stdin);
//freopen(".out","w",stdout);
ios::sync_with_stdio(0);
cin.tie(0);
scanf("%d%d",&n,&m);
for(int i = 1;i <= m;++i)
scanf("%d%d%d",&e[i].u,&e[i].v,&e[i].w);
for(int i = 1;i <= n;++i) fa[i] = i;
sort(e+1,e+m+1);
for(int i = 1;i <= m;++i){
if(find(e[i].u) == find(e[i].v)) continue;
uni(e[i].u,e[i].v);
cnt++;
ans = max(e[i].w,ans);
}
printf("%d %d",cnt,ans);
return 0;
}
[HNOI2006]公路修建问题
题目大意
给出一张无向带权图,每条边可以选择建一级道路或者二级道路,花费为
c
1
c_1
c1和
c
2
c_2
c2。
现在至少要建
k
k
k条一级道路,问生成树中边权最大值最小是多少。
做法
二分最大值,跑 k r u s k a l kruskal kruskal的时候,先把能选的都选成一级道路,剩下的选二级道路,判断是否满足条件。
#include<bits/stdc++.h>
#define ll long long
#define N 10015
#define M 20015
#define inf 0x3f3f3f3f
#define pb push_back
#define mp make_pair
#define lowbit(i) ((i)&(-i))
using namespace std;
int n,k,m,fa[N];
struct node{
int u,v,c1,c2;
}e[M];
int find(int x){
return fa[x]==x?x:fa[x]=find(fa[x]);
}
void uni(int x,int y){
fa[find(x)] = find(y);
}
bool check(int val){
for(int i = 1;i <= n;++i) fa[i] = i;
int cnt = 0;
for(int i = 1;i <= m;++i){
if(find(e[i].u)==find(e[i].v)||e[i].c1 > val) continue;
uni(e[i].u,e[i].v);
cnt++;
}
if(cnt < k) return 0;
for(int i = 1;i <= m;++i){
if(find(e[i].u)==find(e[i].v)||e[i].c2 > val) continue;
uni(e[i].u,e[i].v);
cnt++;
}
if(cnt != n-1) return 0;
return 1;
}
int main(){
//freopen(".in","r",stdin);
//freopen(".out","w",stdout);
ios::sync_with_stdio(0);
cin.tie(0);
cin>>n>>k>>m;
int l = 0,r = 0;
for(int i = 1;i < m;++i){
cin>>e[i].u>>e[i].v>>e[i].c1>>e[i].c2;
r = max(max(e[i].c1,e[i].c2),r);
}
while(l+3<r){
//cout << l << ' ' << r << endl;
int mid = (l+r)>>1;
if(check(mid)) r = mid;
else l = mid;
}
for(int i = l;i <= r;++i){
if(check(i)){
cout<<i;
break;
}
}
return 0;
}
BZOJ3732 Network
题意
给出一张无向图,有
q
q
q次询问,每次问你
u
u
u到
v
v
v所有路径中,边权最大值最小的一条的最大值是多少。
不难想到求出其最小生成树,对于每一个询问,在树上倍增求出
l
c
a
lca
lca的同时求出最小值,复杂度
O
(
q
l
o
g
n
)
O(qlogn)
O(qlogn)
然后我发现了这道题
Loj #137. 最小瓶颈路 加强版
n
≤
7
∗
1
0
4
n \le 7*10^4
n≤7∗104,
q
≤
1
0
7
q \le 10^7
q≤107
刚刚所讲的做法卡卡常数就能莽过去 不能通过此题。
于是我们有了一个新的解法,可以在
O
(
n
l
o
g
n
+
q
)
O(nlogn+q)
O(nlogn+q)的复杂度下通过。
做法
1. k r u s k a l kruskal kruskal重构树
对原图跑一遍 k r u s k a l kruskal kruskal,若 u , v u,v u,v不在同一集合内,新建一个点 t o p top top,合并 u , v , t o p u,v,top u,v,top所在集合,并将 u , v u,v u,v的祖先作为 t o p top top的儿子, t o p top top有点权 w ( u , v ) w(u,v) w(u,v)即 u , v u,v u,v的边权。然后我们就得到了一个重要的性质, l c a ( u , v ) lca(u,v) lca(u,v)的点权就是 u u u到 v v v所有路径中,边权最大值最小的一条的最大值。
2. l c a lca lca
如何快速求出两点之间的
l
c
a
lca
lca?
倍增: 预处理
O
(
n
l
o
g
n
)
O(nlogn)
O(nlogn) 查询
O
(
l
o
g
n
)
O(logn)
O(logn) 显然不行。
于是我们有一种 预处理
O
(
n
l
o
g
n
)
O(nlogn)
O(nlogn) 查询
O
(
1
)
O(1)
O(1)的做法
求出一棵树的欧拉序,然后做静态
R
M
Q
RMQ
RMQ。
欧拉序类似于
d
f
s
dfs
dfs序,只不过欧拉序是进入一次加
1
1
1,回溯也加
1
1
1,然后我们记录下
f
i
r
[
i
]
fir[i]
fir[i]表示第一次访问
i
i
i的欧拉序,我们发现
l
c
a
(
u
,
v
)
lca(u,v)
lca(u,v)就是
f
i
r
[
u
]
fir[u]
fir[u]到
f
i
r
[
v
]
fir[v]
fir[v]之间深度最小的节点,
R
M
Q
RMQ
RMQ维护即可。
关于RMQ
关于lca
#include<bits/stdc++.h>
#define ll long long
#define N 1000015
#define inf 0x3f3f3f3f
#define pb push_back
#define mp make_pair
#define lowbit(i) ((i)&(-i))
using namespace std;
int n,m,q,fa[N],fir[N],dep[N],id[30][N],val[N],lg2[N],tot;
int A,B,C,P;
const int mod = 1e9+7;
inline int rnd(){ return A=(A*B+C)%P;}
struct node{
int u,v,w;
bool operator <(const node &a) const{
return a.w > w;
}
}G[N];
vector<int> e[N];
int find(int x){
return fa[x]==x?x:fa[x]=find(fa[x]);
}
int Kruskal(){
int top = n;
for(int i = 1;i <= 2*n;++i) fa[i] = i;
for(int i = 1;i <= m;++i){
int x = find(G[i].u),y = find(G[i].v);
if(x == y) continue;
//cout << x << " TTT " << y << " " << top+1 <<endl;
val[++top] = G[i].w;
fa[x] = fa[y] = top;
e[top].pb(x);e[top].pb(y);
e[x].pb(top);e[y].pb(top);
}
return top;
}
void dfs(int u,int f){
//cout <<"EEE " << u << endl;
fir[u] = ++tot;
dep[u] = dep[f]+1;
id[0][tot] = u;
for(int i = 0;i < e[u].size();++i){
int v = e[u][i];
if(v == f) continue;
dfs(v,u);
id[0][++tot] = u;
}
}
void RMQ(){
for(int i = 2;i <= tot;++i) lg2[i] = lg2[i>>1]+1;
for(int j = 1;j <= 20;++j){
for(int i = 1;i+(1<<j)-1 <= tot;++i){
if(dep[id[j-1][i]] < dep[id[j-1][i+(1<<(j-1))]])
id[j][i] = id[j-1][i];
else id[j][i] = id[j-1][i+(1<<(j-1))];
}
}
}
int LCA(int u,int v){
int l = fir[u],r = fir[v];
if(l > r) swap(l,r);
int k = lg2[r-l+1];
if(dep[id[k][l]] < dep[id[k][r-(1<<k)+1]]) return id[k][l];
return id[k][r-(1<<k)+1];
}
int main(){
//freopen(".in","r",stdin);
//freopen(".out","w",stdout);
ios::sync_with_stdio(0);
cin.tie(0);
scanf("%d%d",&n,&m);
for(int i = 1;i <= m;++i)
scanf("%d%d%d",&G[i].u,&G[i].v,&G[i].w);
sort(G+1,G+m+1);
int root = Kruskal();
// for(int i = 1;i <= 2*n;++i){
// cout << "fff " << i << ": ";
// for(auto v:e[i]){
// cout << v << ' ';
// }
// cout << endl;
// }
dfs(root,0);
RMQ();
scanf("%d",&q);
scanf("%d%d%d%d",&A,&B,&C,&P);
int ans = 0;
while(q--){
int u,v;
u = rnd()%n+1,v=rnd()%n+1;
//cout << LCA(u,v) << endl;
ans = (ans%mod+val[LCA(u,v)]%mod)%mod;
}
printf("%d",ans);
return 0;
}