【bzoj 4046 加强版】Pork barrel

刚考完以为是神仙题……后来发现好像挺蠢的…… QwQ

题意

  给你一张 \(n\) 个点 \(m\) 条边的无向图(不一定连通),有 \(q\) 组询问,每组询问给你 \(2\) 个正整数 \(l,h\),你需要选出一些边,满足边权都在 \([l,h]\) 范围内,连通尽量多的点对,在此基础上使得边权和最小。
  \(1\le n,m,q\le 10^5,\space 1\le w_i,l,h\le 10^4\)(原题 \(n\le 1000,\space q\le 10^6\)

题解

  动态最小生成树(汗)
  我怎么什么都没写过
  考场上花 10 分钟写了 5 分 \(O(qn)\) 暴力就滚了

  假设这题可以离线,先将所有边按权值从大到小排序。
  依次判断每条边的两端点是否连通,若未连通则连上(这样连通的点对数必定增多),若连通则替换掉一条树上这两点之间的简单路径上 权值最大的一条边(\(\text{cut}\) 一下权值最大的那条边,\(\text{link}\) 一下这条边)。
  加边的同时动态处理询问。对于一组询问 \([L,R]\),若当前插入的边的权值是 \(\ge L\) 的最小值,则我们决定处理这组询问。此时最小生成(森林)上的所有边权都 \(\ge L\),我们直接扔掉所有权值 \(\gt R\) 的边,剩余的边的权值和就是答案。发现边权很小,所以可以用树状数组动态维护最小生成(森林)中的边权,第 \(i\) 位记录所有权值为 \(i\) 的边的权值和,单点修改,区间查询。

  考虑在线,我想这题时居然忘了加边和询问是分开的了……
  依然把边按权值从大到小排序,每加入一条边时,第 \(i\) 个版本继承第 \(i+1\) 个版本,在主席树的第 \(w_i\) 个版本上单点修改即可。在线处理询问 \([L,R]\) 时在主席树的第 \(L\) 个版本上区间查询即可。
  复杂度 \(O(n\log n)\)
  仔细想想好像确实挺水的,所以还是我太菜了

  附注:
  1. \(LCT\) 一般维护点权,边权不太好维护,可以把第 \(i\) 条边转成一个编号为 \(i+n\) 的点,然后加入这条边的时候 把这个点与边的两端点 \(\text{link}\) 一下即可
  2. 注意主席树的空间是 \(O(2m\log w)\) 的!!!不是 \(O(10000\log 10000)\)!!!因为这个 sb 问题调了一小时 zbl

#include<bits/stdc++.h>
#define N 200010
using namespace std;
inline int read(){
    int x=0; bool f=1; char c=getchar();
    for(;!isdigit(c);c=getchar()) if(c=='-') f=0;
    for(; isdigit(c);c=getchar()) x=(x<<3)+(x<<1)+c-'0';
    if(f) return x; return 0-x;
}
int n,m,online,q;
struct inp{int u,v,w;}a[N<<1];
inline bool cmp(inp a, inp b){
    return a.w>b.w;
}
namespace LCT{
    #define son(x,fx) tr[x].son[fx]
    struct Tree{int fa,son[2],v,mx_id; bool r;}tr[N<<1];
    inline bool isRoot(int x){
        return son(tr[x].fa,0)!=x && son(tr[x].fa,1)!=x;
    }
    inline bool idf(int x){
        return son(tr[x].fa,1)==x;
    }
    void pushup(int x){
        tr[x].mx_id=x;
        if(tr[tr[son(x,0)].mx_id].v > tr[tr[x].mx_id].v) tr[x].mx_id=tr[son(x,0)].mx_id;
        if(tr[tr[son(x,1)].mx_id].v > tr[tr[x].mx_id].v) tr[x].mx_id=tr[son(x,1)].mx_id;
    }
    void pushdown(int x){
        if(tr[x].r){
            swap(son(x,0),son(x,1));
            tr[son(x,0)].r^=1, tr[son(x,1)].r^=1;
            tr[x].r=0;
        }
    }
    void pd(int x){
        if(!isRoot(x)) pd(tr[x].fa);
        pushdown(x);
    }
    inline void connect(int x, int f, int fx){
        tr[x].fa=f, son(f,fx)=x;
    }
    void rotate(int x){
        int y=tr[x].fa, z=tr[y].fa, idf_x=idf(x), idf_y=idf(y), B=son(x,idf_x^1);
        if(!isRoot(y)) connect(x,z,idf_y);
        else tr[x].fa=z;
        connect(B,y,idf_x), connect(y,x,idf_x^1);
        pushup(y), pushup(x);
    }
    void splay(int x){
        pd(x);
        int f;
        for(; !isRoot(x); ){
            f=tr[x].fa;
            if(!isRoot(f)) rotate(idf(x)==idf(f) ? f : x);
            rotate(x);
        }
    }
    void access(int x){
        for(int y=0; x; x=tr[y=x].fa){
            splay(x);
            son(x,1)=y;
            pushup(x);
        }
    }
    void makeroot(int x){
        access(x), splay(x), tr[x].r^=1;
    }
    void link(int x, int y){
        makeroot(x);
        tr[x].fa=y;
    }
    int cutMax(int x, int y){
        makeroot(x);
        access(y);
        splay(y);
        x=tr[y].mx_id;
        splay(x);
        tr[son(x,0)].fa=tr[son(x,1)].fa=0;
        son(x,0)=son(x,1)=0;
        return x;
    }
    #undef son
}using namespace LCT;
namespace PT{
    #define son(x,fx) tr[o].son[fx]
    int cnt,rt[100010];
    struct Tree{int son[2],sum;}tr[200010*20];
    void ins(int &o, int pre, int l, int r, int x, int v){
        tr[o=++cnt] = tr[pre], tr[o].sum += v;
        if(l==r) return;
        int mid=l+r>>1;
        if(x<=mid) ins(son(o,0),son(pre,0),l,mid,x,v);
        else ins(son(o,1),son(pre,1),mid+1,r,x,v);
    }
    int query(int o, int l, int r, int L, int R){
        if(!o) return 0;
        if(L<=l && r<=R) return tr[o].sum;
        int mid=l+r>>1, ret=0;
        if(L<=mid) ret+=query(son(o,0),l,mid,L,R);
        if(R>mid) ret+=query(son(o,1),mid+1,r,L,R);
        return ret;
    }
}using namespace PT;
namespace DSU{
    int fa[N];
    void init(){
        for(int i=1; i<=n; ++i) fa[i]=i;
    }
    int find(int x){
        if(fa[x]==x) return x;
        return fa[x]=find(fa[x]);
    }
}using namespace DSU;
int main(){
    n=read(), m=read(), online=read();
    for(int i=1; i<=m; ++i) a[i].u=read(), a[i].v=read(), a[i].w=read();
    sort(a+1,a+m+1,cmp);
    init();
    int fu,fv;
    for(int i=1; i<=m; ++i){
        rt[a[i].w]=rt[a[i-1].w];
        fu=find(a[i].u), fv=find(a[i].v);
        if(fu==fv){
            q=cutMax(a[i].u,a[i].v);
            ins(rt[a[i].w],rt[a[i].w],1,a[1].w,LCT::tr[q].v,-LCT::tr[q].v);
        }
        else{
            DSU::fa[fv]=fu;
            q=n+i;
        }
        LCT::tr[q].v=a[i].w;
        LCT::tr[q].mx_id=q;
        link(a[i].u,q), link(q,a[i].v);
        ins(rt[a[i].w],rt[a[i].w],1,a[1].w,a[i].w,a[i].w);
    }
    for(int i=a[1].w-1; i>0; --i)
        if(rt[i]==0) rt[i]=rt[i+1];
    
    q=read();
    int l,r,lastans=0;
    while(q--){
        l=read()-online*lastans, r=read()-online*lastans;
        printf("%d\n",lastans=query(rt[l],1,a[1].w,1,r));
    }
    return 0;
}
/*
5 7 1
1 2 2
2 3 4
3 4 3
4 5 1
5 1 3
2 5 4
1 4 5
5
1 2
4 7
11 12
11 13
18 19
*/

转载于:https://www.cnblogs.com/scx2015noip-as-php/p/bzoj4046.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值