Kruskal重构树

Kruskal重构树就是一个图利用最小生成树算法建树,但是建树代码如下

    int tot=n;
    for(i=1;i<=m;i++){
        int fx=find(edge[i].x);
        int fy=find(edge[i].y);
        if(fx!=fy){
            pre[fx]=pre[fy]=++tot;
            kv[tot].push_back(fx);
            kv[tot].push_back(fy);
            val[tot]=edge[i].val;
        }
        if(tot==2*n-1)
      break;
    }

这样的话重构树其实是有2n-1个节点的,而且这个树的叶子节点都是1到n的原图节点,非叶子节点是标号为[n+1,2n-1]的节点,也就是重构树新建的节点,这些节点的点权值就是之前的边权,而且如果按最小生成树的话,这个树本质是最大堆,最大生成树则相反,为最小堆。
Kruskal重构树适用于求一个无向图,然后给你个点,让你在只能经过边权大于等于x(或者小于x,都差不多,无非是最大生成树还是最小生成树的问题)的边,求能到达的顶点的一些性质(列如个数,权值最大等等,可以通过维护数据结构,虚树等等乱搞)。

方块为扩展的点,即[n+1,2*n-1]这块的点,权值为两个子节点的原图的边权,最终重构树的根节点是2 * n-1.
这样假设我从1出发,只能经过边权小于等于3的边,那么我就在1的父亲节点里找最后一个小于等于3的边即可(因为这是一个大根堆),然后找到了这个父节点假设为fa,那么fa的子树上的叶子节点都是从1满足限制条件能到达的节点,然后就可以通过线段树+dfs序搞最大值啥的。
然后找父节点时要倍增,dp[MAX_N][21],然后

	for(i=20;i>=0;i--){
		if(val[dp[x][i]]的权值满足限制条件)
		x=dp[x][i]}
	//x即为fa节点

牛客 水灾
这个题恶心人,卡常,我过了90%的测试点,把求lca的改成o(n)的应该就能A了,懒得改了。重构树加虚树比如容易理解而且复杂度也是n log的,常数有点大,但是好想点。

#include<iostream>
#include<cstdio>
#include<vector>
#include<algorithm>
#include<cmath>
using namespace std;
const int MAX_N=1001000;
int pre[MAX_N];
struct skt{
    int from,to,val;
}edge[MAX_N/2];
//vector<int>v[MAX_N];
int head[MAX_N],ver[MAX_N],Next[MAX_N],tott;
void add(int x,int y){
    ver[++tott]=y;Next[tott]=head[x];head[x]=tott;
}
bool cmp(skt a,skt b){
    return a.val>b.val;
}
int cnt=0,dfn[MAX_N];
bool cmpp(int x,int y){
    return dfn[x]<dfn[y];
}
int aa[MAX_N];
inline int find(int x){
    if(x!=pre[x])
    pre[x]=find(pre[x]);
    return pre[x];
}
int dp[MAX_N][21],dep[MAX_N],n,log_2[MAX_N];
void dfs(int now,int fa){
    int i;
    dfn[now]=++cnt;
    //cout<<now<<"\n";
    //for(i=0;i<v[now].size();i++){
    for(i=head[now];i;i=Next[i]){ 
        int to=ver[i];
        if(to==fa)
        continue;
        dp[to][0]=now;
        dep[to]=dep[now]+1;
        dfs(to,now);
    }
}
int LCA(int a,int b){
    int k=log_2[2*n-1],i;
    if(dep[a]<dep[b]){
        int c=a;
        a=b;
        b=c;
    }
    while(dep[a]!=dep[b]){
        for(i=k;i>=0;i--){
            if(dep[dp[a][i]]>=dep[b])
            a=dp[a][i];
        }
    }
    if(a==b){
        return b;
    }
    for(i=k;i>=0;i--){
        if(dp[a][i]!=dp[b][i]){
            a=dp[a][i];
            b=dp[b][i];
        }
    }
    return dp[a][0];
}
int val[MAX_N];
inline char nc() {
    static char buf [100000],*p1=buf,*p2=buf;
    return p1==p2&&(p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++;
}
inline int read() {
    static char c=nc(); int x=0,f=1;
    for(;c>'9'||c<'0';c=nc()) if(c=='-') f=-1;
    for(;c<='9'&&c>='0';c=nc()) x=(x<<3)+(x<<1)+c-48;
    return x*f;
}
inline void out(int a){  
    if(a < 0) {putchar('-'); a = -a;}  
    if(a >= 10)out(a / 10);  
    putchar(a % 10 + '0');  
}  
int main(void){
    int m,q,i,j,k;
    scanf("%d%d%d",&n,&m,&q);
    for(i=1;i<=m;i++){
        edge[i].from=read();
        edge[i].to=read();
        edge[i].val=read();
    }
    //scanf("%d%d%d",&edge[i].from,&edge[i].to,&edge[i].val);
    sort(edge+1,edge+m+1,cmp);
    int tot=n;
    for(i=1;i<=2*n;i++){
        pre[i]=i;
        log_2[i]=log_2[i-1]+(1<<log_2[i-1]==i);
    }
    for(i=1;i<=n;i++)
    val[i]=-1;
    for(i=1;i<=m;i++){
        int fx=find(edge[i].from);
        int fy=find(edge[i].to);
        if(fx!=fy){
            pre[fx]=pre[fy]=++tot;
            val[tot]=edge[i].val;
            //cout<<fx<<" "<<tot<<" "<<fy<<"\n";
            //v[fx].push_back(tot);
            //v[tot].push_back(fx);
            //v[fy].push_back(tot);
            //v[tot].push_back(fy);
            add(tot,fx);
            add(tot,fy);
        }
        if(tot==2*n-1)
        break;
    }
    dfs(2*n-1,0);
    dep[0]=-1;
    for(i=1;i<=log_2[2*n-1];i++){
        for(j=1;j<=2*n-1;j++){
            dp[j][i]=dp[dp[j][i-1]][i-1];
        }
    }
    int lastans=0;
    for(i=1;i<=q;i++){
        //scanf("%d",&k);
        k=read();
        for(j=1;j<=k;j++){
            //scanf("%d",&aa[j]);
            aa[j]=read();
            aa[j]=aa[j]^lastans;
        }
        sort(aa+1,aa+k+1,cmpp);
        int ans=0;
        for(j=2;j<=k;j++){
            int lca=LCA(aa[j-1],aa[j]);
            ans=max(ans,val[lca]);
        }
        lastans=ans;
        out(lastans);
        putchar('\n');
    }
    return 0;
}

P4768 [NOI2018]归程
求出从1出发的dijkstra,然后每个询问找那个fa节点,然后在他的子树里找dis的最小值即可,前14个测试点很好过,后两个有点恶心人,4s时限,用了快读加o2优化3.99s,不过可以把vector改了,但是太麻烦了。而且多组输入尽量别用memset,然后注意点清空就ok了。

#include<iostream>
#include<cstdio>
#include<vector>
#include<algorithm>
#include<queue>
using namespace std;
const int MAX_N=201000;
const int INF=0x3f3f3f3f;
vector<int>v[MAX_N],kv[2*MAX_N],w[MAX_N];
int N;
inline int min(int x,int y){
 return x<y?x:y;
}
inline int max(int x,int y){
 return x<y?x:y;
} 
struct skt{
 int x,y,dep;
}edge[2*MAX_N];
struct sktt{
 int x,d;
 friend bool operator<(sktt a,sktt b){
  return a.d>b.d;
 }
};
int dis[MAX_N];
bool vis[MAX_N];
bool cmp(skt a,skt b){
 return a.dep>b.dep;
}
void dijkstra(int x){
 for(int i=1;i<=N;i++){
  dis[i]=INF;
  vis[i]=false;
 }
 dis[x]=0;
 priority_queue<sktt>q;
 sktt t;
 t.x=x;t.d=0;
 q.push(t);
 while(!q.empty()){
  t=q.top();
  q.pop();
  x=t.x;
  if(vis[x])
  continue;
  vis[x]=true;
  for(int i=0;i<v[x].size();i++){
   int y=v[x][i];
   int cost=w[x][i];
   if(dis[y]>dis[x]+cost){
    dis[y]=dis[x]+cost;
    t.x=y;
    t.d=dis[y];
    q.push(t);
   }
  }
 }
}
int pre[2*MAX_N];
int find(int x){
 if(x!=pre[x])
 pre[x]=find(pre[x]);
 return pre[x];
}
int cnt=0,in[2*MAX_N],out[2*MAX_N],pos[2*MAX_N];
int dp[2*MAX_N][21];
void dfs(int x){
 int i;
 in[x]=++cnt;
 pos[cnt]=x;
 for(i=0;i<kv[x].size();i++){
  int y=kv[x][i];
  dp[y][0]=x;
  dfs(y);
 }
 out[x]=cnt;
}
int val[2*MAX_N];
void init(){
 int i,j;
 for(j=1;j<=20;j++){
  for(i=1;i<=2*N-1;i++){
   dp[i][j]=dp[dp[i][j-1]][j-1];
  }
 }
}
struct node{
  int l,r;
 int minl;
}a[MAX_N*8];
void update(int k){
  a[k].minl=min(a[k<<1].minl,a[k<<1|1].minl);
}
void build(int k,int l,int r){
  a[k].l=l;a[k].r=r;
  if(l==r){
   if(pos[l]>N)
   a[k].minl=INF;
   else
    a[k].minl=dis[pos[l]];
    return;
  }
  int mid=(l+r)>>1;
  build(k<<1,l,mid);
  build(k<<1|1,mid+1,r);
  update(k);
}
inline int query(int k,int l,int r){
  if(a[k].l>=l&&a[k].r<=r)
  return a[k].minl;
  int mid=(a[k].l+a[k].r)>>1;
  int x=INF;
  if(r>mid)
  x=min(x,query(k<<1|1,l,r));
  if(l<=mid)
  x=min(x,query(k<<1,l,r));
  return x;
}
inline int work(int x,int h){
 int i;
 for(i=20;i>=0;i--){
  if(val[dp[x][i]]>h)
  x=dp[x][i];
 }
 return query(1,in[x],out[x]);
}
//inline char nc() {
// static char buf [100000],*p1=buf,*p2=buf;
// return p1==p2&&(p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++;
//}
//inline int read(){
// static char c=nc(); int x=0,f=1;
// for(;c>'9'||c<'0';c=nc()) if(c=='-') f=-1;
// for(;c<='9'&&c>='0';c=nc()) x=(x<<3)+(x<<1)+c-48;
// return x*f;
//}
inline int read() {
    int x=0;
    char c=getchar();
    while(c<'0'||c>'9') c=getchar();
    while(c>='0'&&c<='9') x=x*10+c-'0', c=getchar();
    return x;
}
inline void outt(int x){  
    if(x < 0) {putchar('-'); x = -x;}  
    if(x >= 10)outt(x / 10);  
    putchar(x % 10 + '0');  
}
int main(void){
 int n,m,i,x,y,z,h;
 int T;
 //scanf("%d",&T);
 T=read();
 while(T--){
 //scanf("%d%d",&n,&m);
 n=read();m=read();
 N=n;
 for(i=1;i<=n;i++){
  v[i].clear();
  w[i].clear();
 }
 for(i=1;i<=2*n;i++){
  kv[i].clear();
  val[i]=0;
 }
 for(i=1;i<=m;i++){
  //scanf("%d%d%d%d",&x,&y,&z,&h);
  x=read();y=read();z=read();h=read();
  edge[i].x=x;edge[i].y=y;edge[i].dep=h;
  v[x].push_back(y);
  w[x].push_back(z);
  v[y].push_back(x);
  w[y].push_back(z);
 }
 sort(edge+1,edge+m+1,cmp);
 dijkstra(1);
 for(i=1;i<=2*n;i++)
 pre[i]=i;
 int tot=n;
 int s=2*n-1;
 for(i=1;i<=m;i++){
  int fx=find(edge[i].x);
  int fy=find(edge[i].y);
  if(fx!=fy){
   pre[fx]=pre[fy]=++tot;
   kv[tot].push_back(fx);
   kv[tot].push_back(fy);
   val[tot]=edge[i].dep;
  }
  if(tot==2*n-1)
  break;
 }
 cnt=0;
 dfs(s);
 init();
 build(1,1,2*n-1);
 int q;
 long long lastans=0,K,S,v,p;
 //scanf("%d%lld%lld",&q,&K,&S);
 q=read();K=read();S=read();
 for(i=1;i<=q;i++){
  //scanf("%lld%lld",&v,&p);
  v=read();p=read();
  v=(v+K*lastans-1)%n+1;
  p=(p+K*lastans)%(S+1);
  //cout<<v<<" "<<p<<"\n";
  lastans=work(v,p);
  //printf("%lld\n",lastans);
  outt(lastans);
  putchar('\n');
 }
 }
 return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值