P1967 货车运输
作者:互联网
容易发现此题中需要找到尽可能大的边来走?
那么我们求一棵最大生成树,把必要的边保留下来就行了。
之后要求两点间的最小边权?
你可以选择倍增,可以选择树剖,但我选择拉LCT的板子…(指针警告&封装警告&STL画风警告)
似乎理论复杂度优秀但常数巨大???(1000+ms without O2)
所以简单讲一下做法
Kruskal求最大生成树的时候把选中的边在LCT上连起来就行了。
由于LCT存的是点权,而此题要求边权,那么把边都建成点就好了。。。
然后原图中的点权要设为无穷大以防影响答案。。。
由于我的LCT高度封装,所以每次拉板子只要做很小的改动即可
所以这个题解的亮点也在板子吧
然而我的板子也因此很长,为了防止影响阅读体验,先放主要代码再放板子。
算法主要代码,暂时不想学LCT的看这一段就行了。还不行就当伪代码看吧
template<typename Argument_type,typename Result_type>//板子要用仿函数来实现运算
//这个写法STL也需要,口以康康
class Min
{
public:
Result_type operator()(const Argument_type& x,const Argument_type& y)const
{
return std::min(x,y);
}
};
template<typename T>
void read(T& x)
{
bool f=0;
x=0;
char c=std::getchar();
while(c<'0'||c>'9')f|=(c=='-'),c=getchar();
while(c>='0'&&c<='9')x=x*10+(c^48),c=getchar();
}
template<typename T>
void write(T x)
{
if(x<0)putchar('-'),x=-x;
if(x>=10)write(x/10);
putchar(x%10^48);
}
const int N=100005,M=500005;
int n,m,fa[N];
int find(int x)
{
if(x==fa[x])return x;
return fa[x]=find(fa[x]);
}
struct Edge
{
int from,to,w;
bool operator<(const Edge& rhs)const
{
return w<rhs.w;
}
};
Edge E[M];
bool cmp(const Edge& a,const Edge& b)
{
return b<a;
}
link_cut_tree<int,Min<int,int> > my_LCT;
link_cut_tree<int,Min<int,int> >::iterator iters[N];//迭代器指向原图中的点在LCT中的对应点
void kruskal()
{
std::sort(E,E+m,cmp);
for(int i=0;i<m;++i)
{
int u=E[i].from,v=E[i].to;
int fu=find(u),fv=find(v);
if(fu==fv)continue;
fa[fu]=fv;
link_cut_tree<int,Min<int,int> >::iterator it=my_LCT.make_node(E[i].w);
my_LCT.link(iters[u],it);
my_LCT.link(it,iters[v]);
}
}
int main()
{
read(n);
read(m);
for(int i=1;i<=n;++i)
{
iters[i]=my_LCT.make_node(0x7fffffff);
fa[i]=i;
}
for(int i=0;i<m;++i)
{
read(E[i].from);
read(E[i].to);
read(E[i].w);
}
kruskal();
int q;
read(q);
while(q--)
{
int u,v;
read(u),read(v);
std::pair<bool,int> res=my_LCT.query(iters[u],iters[v]);//为了判不连通的情况,查询返回的是pair,第一项表示是否连通
write(res.first?res.second:-1);
putchar('\n');
}
}
LCT板子,难打但便于复制和重用,喜欢拿去,看不懂的话我博客里有一篇专门讲LCT的可以康康。
平时做题用用算了,考试打这个是作死
#include<cstdio>
#include<iostream>
#include<algorithm>
//#define MEMPOOL //内存池开关,去掉注释就可以用内存池,但实测效果不佳
template<typename Value_type,typename Functor>
class LCT_splay
{
public:
struct Node;
Node* __new_node(const Value_type&);
#ifdef MEMPOOL
private:
Node mem_pool[1<<20];
int tot;
#endif
};
template<typename Value_type,typename Functor>
struct LCT_splay<Value_type,Functor>::Node:Functor
{
Value_type val,sum;
Node* ftr;
Node* ch[2];
// Node*& lc;
// Node*& rc;
#define lc ch[0]//省空间写法
#define rc ch[1]
bool rev;
Node(const Value_type& v=Value_type()):
val(v),
sum(v),
ftr(NULL),
// lc(ch[0]),
// rc(ch[1]),
rev(0) {ch[0]=ch[1]=NULL;}
void reverse();
void push_down();
void push_all();
void maintain();
bool is_root();
void rotate();
void splay();
};
template<typename Value_type,typename Functor>
void LCT_splay<Value_type,Functor>::Node::reverse()
{
rev^=1;
}
template<typename Value_type,typename Functor>
void LCT_splay<Value_type,Functor>::Node::push_down()
{
if(!rev)return;
rev=0;
Node* ptr=lc;
lc=rc;
rc=ptr;
if(lc!=NULL)lc->reverse();
if(rc!=NULL)rc->reverse();
}
template<typename Value_type,typename Functor>
void LCT_splay<Value_type,Functor>::Node::push_all()
{
if(!is_root())this->ftr->push_all();
push_down();
}
template<typename Value_type,typename Functor>
void LCT_splay<Value_type,Functor>::Node::maintain()
{
sum=val;
if(lc!=NULL)sum=Functor::operator()(lc->sum,sum);
if(rc!=NULL)sum=Functor::operator()(sum,rc->sum);
}
template<typename Value_type,typename Functor>
bool LCT_splay<Value_type,Functor>::Node::is_root()
{
return ftr==NULL||(ftr->lc!=this&&ftr->rc!=this);
}
template<typename Value_type,typename Functor>
void LCT_splay<Value_type,Functor>::Node::rotate()
{
Node *nftr=ftr,*gftr=ftr->ftr;
bool is_rc=nftr->rc==this;
bool is_rf=gftr!=NULL?gftr->rc==nftr:0;
ftr=gftr;
if(!nftr->is_root())gftr->ch[is_rf]=this;
nftr->ch[is_rc]=this->ch[!is_rc];
if(this->ch[!is_rc]!=NULL)this->ch[!is_rc]->ftr=nftr;
nftr->ftr=this;
this->ch[!is_rc]=nftr;
nftr->maintain();
maintain();
}
template<typename Value_type,typename Functor>
void LCT_splay<Value_type,Functor>::Node::splay()
{
push_all();
while(!is_root())
{
Node *nftr=ftr,*gftr=ftr->ftr;
if(nftr->is_root())rotate();
else
{
if((gftr->lc==nftr)^(nftr->lc==this))rotate();
else nftr->rotate();
rotate();
}
}
}
template<typename Value_type,typename Functor>
typename
LCT_splay<Value_type,Functor>::Node* LCT_splay<Value_type,Functor>::__new_node(const Value_type& v)
{
#ifdef MEMPOOL
if(tot==1<<20)
{
fprintf(stderr,"Error:No enough memory\n");
return NULL;
}
mem_pool[tot++].val=v;
return mem_pool+tot-1;
#else
return new Node(v);
#endif
}
template<typename Value_type,typename Functor>
class link_cut_tree:public LCT_splay<Value_type,Functor>
{
typedef typename LCT_splay<Value_type,Functor>::Node Node;
private:
void access(Node*);
void make_root(Node*);
Node* find_root(Node*);
bool split(Node*,Node*);
public:
struct iterator;
iterator make_node(const Value_type&);
bool link(const iterator&,const iterator&);
bool cut(const iterator&,const iterator&);
std::pair<bool,Value_type> query(iterator,iterator);
bool modify(iterator,const Value_type&);
};
template<typename Value_type,typename Functor>
struct link_cut_tree<Value_type,Functor>::iterator
{
private:
Node* ptr;
friend class link_cut_tree;
public:
Value_type operator*()const{return ptr->val;}
iterator(Node* p=NULL):ptr(p) {}
iterator(const iterator& iter):ptr(iter.ptr) {}
};
template<typename Value_type,typename Functor>
void link_cut_tree<Value_type,Functor>::access(Node* ptr)
{
for(Node* nptr=NULL;ptr!=NULL;nptr=ptr,ptr=ptr->ftr)
{
ptr->splay();
ptr->rc=nptr;
ptr->maintain();
}
}
template<typename Value_type,typename Functor>
void link_cut_tree<Value_type,Functor>::make_root(Node* ptr)
{
access(ptr);
ptr->splay();
ptr->reverse();
}
template<typename Value_type,typename Functor>
typename
link_cut_tree<Value_type,Functor>::Node* link_cut_tree<Value_type,Functor>::find_root(Node* ptr)
{
access(ptr);
ptr->splay();
while(ptr->lc!=NULL)ptr->push_down(),ptr=ptr->lc;
ptr->splay();
return ptr;
}
template<typename Value_type,typename Functor>
bool link_cut_tree<Value_type,Functor>::split(Node* sptr,Node* eptr)
{
make_root(sptr);
if(find_root(eptr)!=sptr)return 0;
eptr->splay();
return 1;
}
template<typename Value_type,typename Functor>
typename
link_cut_tree<Value_type,Functor>::iterator link_cut_tree<Value_type,Functor>::make_node(const Value_type& v)
{
return iterator(LCT_splay<Value_type,Functor>::__new_node(v));
}
template<typename Value_type,typename Functor>
bool link_cut_tree<Value_type,Functor>::link(const iterator& siter,const iterator& eiter)
{
Node* sptr=siter.ptr;
Node* eptr=eiter.ptr;
make_root(sptr);
if(find_root(eptr)==sptr)return 0;
sptr->ftr=eptr;
return 1;
}
template<typename Value_type,typename Functor>
bool link_cut_tree<Value_type,Functor>::cut(const iterator& siter,const iterator& eiter)
{
Node* sptr=siter.ptr;
Node* eptr=eiter.ptr;
make_root(sptr);
if(find_root(eptr)!=sptr||eptr->ftr!=sptr||eptr->lc!=NULL)return 0;
eptr->ftr=NULL;
sptr->lc=NULL;
sptr->maintain();
return 1;
}
template<typename Value_type,typename Functor>
std::pair<bool,Value_type> link_cut_tree<Value_type,Functor>::query(iterator siter,iterator eiter)
{
Node* sptr=siter.ptr;
Node* eptr=eiter.ptr;
if(!split(sptr,eptr))return std::make_pair(0,Value_type());
return std::make_pair(1,eptr->sum);
}
template<typename Value_type,typename Functor>
bool link_cut_tree<Value_type,Functor>::modify(iterator iter,const Value_type& v)
{
Node* ptr=iter.ptr;
if(ptr==NULL)return 0;
ptr->splay();
ptr->val=v;
ptr->maintain();
return 1;
}
#undef lc
#undef rc
开始用了自己考场上打的LCT结果各种WA和RE,最后还是懒癌发作拖了板子。。。
最后要说几句:
LCT大法好!
指针大法好!
封装大法好!
end.
标签:Node,const,iterator,货车运输,splay,template,P1967,ptr 来源: https://blog.csdn.net/qq_42722211/article/details/96424992