前置芝士
1.认识什么是图
2.认识什么是树
3.了解kruskal
4.倍增
定义
我乱理解的
kruskal重构树是在基于kruskal思想上进行重构(即加上虚点)得到的一棵二叉树,且符合二叉堆性质。
这些虚点其实就是生成树上两个点的边权变为了点权
方法
这里以最小生成树为例子,先用kruskal的方法得到一些边,在加边时用并查集维护,即先找到它们的祖先,然后新建一个点,向它们的祖先连边
现在有以下性质:
1.kruskal总共有2n-1个点,且实点(感性理解,非虚点)是一定在叶子上的,因此可以得出对于两个点的LCA的点权一定是这张图中最短路径中最长的边权值
2.每个非叶子点都代表一条边
根据此,可以求出一些最..最..问题。。。
例题
1.货车运输
A 国有 n 座城市,编号从 1 到 n,城市之间有 m 条双向道路。每一条道路对车辆都有重量限制,简称限重。
现在有 q 辆货车在运输货物, 司机们想知道每辆车在不超过车辆限重的情况下,最多能运多重的货物。
当学会kruskal重构树后,可以很容易发现其实这就是要在二叉树上做倍增即可
#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int MAXN = 2e4 + 3;
const int MAXM = 5e4 + 3;
const int M = 1e7 + 3;
inline char GetChar(){
static char buf[10001],*p1=buf,*p2=buf;
return p1==p2&&(p2=(p1=buf)+fread(buf,1,10001,stdin),p1==p2)?EOF:*p1++;
}
inline void Read(int &n){
short f=1;
long long x=0;
char c=GetChar();
while(isdigit(c)==false){
if(c=='-'){
f=-1;
}
c=GetChar();
}
while(isdigit(c)==true){
x=((x<<3)+(x<<1)+(c^48));
c=GetChar();
}
n=x*f;
}
int n , m , q , son[MAXN][2] , f[MAXN][23];
struct edge{
int x , y , w;
friend bool operator < ( edge a , edge b ){
return a.w > b.w;
}
}G[MAXM];
int fa[MAXN];
int findSet( int x ){
if( x != fa[x] ) fa[x] = findSet( fa[x] );
return fa[x];
}
int dep[MAXN];
void dfs( int x ){
if( !son[x][0] ) return ;
for( int i = 0 ; i <= 1 ; i ++ ){
int v = son[x][i];
dep[v] = dep[x] + 1;
dfs( v );
}
}
void jump( int &x , int mu ){
for( int i = 20; i >= 0 ; i-- ){
if( dep[f[x][i]] >= mu ) x = f[x][i];
}
}
int LCA( int x , int y ){
if( dep[x] < dep[y] ) swap( x , y );
if( dep[x] != dep[y] ) jump( x , dep[y] );
for( int i = 20 ; i>= 0; i -- ){
if(f[x][i] != f[y][i] )
x = f[x][i] , y = f[y][i];
}
return f[x][0];
}
ll c[MAXN];
int main(){
scanf( "%d%d" , &n , &m );
for( int i = 1 ; i <= m ; i ++ ){
int x , y , z;scanf( "%d%d%d" , &x , &y , &z );
G[i].x = x , G[i].y = y , G[i].w = z;
}
sort( G + 1 , G + m + 1 );
int k = 0 ,ncnt = n;
for( int i = 1 ; i <= n * 2 - 1; i ++ ) fa[i] = i;
for( int i = 1 ; i <= m ;i ++ ){
int x = G[i].x , y = G[i].y , w = G[i].w;
int u = findSet( x ) , v = findSet( y );
if( u == v ) continue;
fa[u] = fa[v] = ++ncnt;
son[ncnt][0] = u , son[ncnt][1] = v;
f[u][0] = f[v][0] = ncnt;
c[ncnt-n] = w;
k ++ ;
if( k == n - 1 ) break;
}
dep[ncnt] = 1;
dfs( ncnt );
for( int j = 1; j <= 20 ; j ++ )
for( int i = 1; i <= ncnt ; i ++ )
f[i][j] = f[f[i][j-1]][j-1];
scanf( "%d" , &q );
while( q -- ){
int x , y;scanf( "%d%d" , &x , &y );
if( findSet( x) != findSet( y ) ) {
printf( "-1\n" );
continue;
}
int lca = LCA( x , y );
printf( "%d\n" , c[lca-n] );
}
return 0;
}
例题2:归程
当发现这道题是一道kruskal重构树的时候,就已经完成一半了
毕竟是一道签到题,难度也不是太大
#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int MAXN = 2e5 + 3;
const int MAXM = 4e5 + 3;
int n , m , head[MAXN] , cnt = 1;
ll dis[MAXN];
struct node{
int x , y , l , a;
friend bool operator < ( node a , node b ){
return a.a > b.a;
}
}E[MAXM];
bool flag[MAXM] , vis[MAXN];
struct edge{
int nex ,to , l;
}G[MAXM<<1];
int f[MAXN<<1][24] , fa[MAXN<<1] , minn[MAXN<<1][24] , son[MAXN<<1][2] , dep[MAXN<<1];
ll dminn[MAXN<<1];
int findSet( int x ){
if( x != fa[x] ) fa[x] = findSet( fa[x] );
return fa[x];
}
struct node1{
int x;ll dis;
node1(){}
node1( int X , ll D ){
x = X , dis = D;
}
friend bool operator < ( node1 a , node1 b ){
return a.dis > b.dis;
}
};
priority_queue<node1>q;
void add_edge( int x , int y , int l ){
G[cnt].to = y , G[cnt].l = l , G[cnt].nex = head[x];
head[x] = cnt ++;
}
void dfs( int x ){
if( !son[x][0] ){
dminn[x] = dis[x];
return ;
}
for( int i = 0 ; i <= 1 ; i ++ ){
int v = son[x][i];
dep[v] = dep[x] + 1;
dfs( v );
}
dminn[x] = min( dminn[son[x][0]] , dminn[son[x][1]] );
}
int main(){
//freopen( "2.in" , "r" , stdin );
//freopen( "2.out" , "w" , stdout );
int T;
scanf( "%d" , &T );
while( T -- ){
memset( flag , 0 , sizeof( flag ) );
memset( head , 0 , sizeof( head ) );
memset( vis , 0 , sizeof( vis) );
memset( son , 0 , sizeof( son ) );
memset( dep , 0 , sizeof( dep ) );
memset( f , 0 , sizeof( f ) );
memset( minn , 0 , sizeof( minn ) );cnt = 1;
scanf( "%d%d" , &n , &m );
for( int i = 1 ; i <= m ; i ++ ){
scanf( "%d%d%d%d" , &E[i].x , &E[i].y , &E[i].l , &E[i].a );
add_edge( E[i].x , E[i].y , E[i].l );
add_edge( E[i].y , E[i].x , E[i].l );
}
sort( E + 1 , E + m + 1 );
for( int i = 1 ; i <= n * 2 ; i ++ ) fa[i] = i;
for( int i = 1 ; i <= n ; i ++ ) dis[i] = 0x7f7f7f7f7f;
dis[1] = 0;
q.push( node1( 1 , 0 ) );
while( !q.empty() ){
node1 tx = q.top();q.pop();
int x = tx.x;
if( vis[x] ) continue;
vis[x] = 1;
for( int i = head[x] ; i ; i = G[i].nex ){
int v = G[i].to , w = G[i].l;
if( dis[v] > dis[x] + w ){
dis[v] = dis[x] + w;
q.push( node1( v , dis[v] ) );
}
}
}
int ncnt = n , k = 0;
for( int i = 1 ; i <= m ; i ++ ){
int x = E[i].x, y = E[i].y , w = E[i].a;
int u = findSet( x ) , v = findSet( y );
if( u == v ) continue;
fa[u] = fa[v] = ++ncnt;
son[ncnt][0] = u , son[ncnt][1] = v;
k ++ ;
f[u][0] = f[v][0] = ncnt;minn[u][0] = minn[v][0] = w;
if( k == n - 1) break;
}
//for( int i = 1 ; i < ncnt ; i ++ )
// printf( "%d\n" , minn[i][0] );
dep[ncnt] = 1;
dfs( ncnt );
for( int j = 1 ; j <= 22 ; j ++ ){
for( int i = 1 ; i <= ncnt ; i ++ ){
f[i][j] = f[f[i][j-1]][j-1];
minn[i][j] = min( minn[f[i][j-1]][j-1] ,minn[i][j-1] );
}
}
ll Q , K , S, lastans = 0;
scanf( "%lld%lld%lld" , &Q , &K , &S );
while( Q -- ){
ll v0 , p0;scanf( "%lld%lld" , &v0 , &p0 );
int v = ( v0 + K * lastans - 1 ) % n + 1;
int p = ( p0 + K * lastans ) % ( S + 1 );
//printf( "%d\n" , p );
for( int i = 22 ; i >= 0 ; i -- ){
if( dep[f[v][i]] && minn[v][i] > p )
v = f[v][i];
}
printf( "%lld\n" , dminn[v] );
lastans = dminn[v];
}
}
}