BZOJ 4046 [Cerc2014] Pork barre

題目大意:給你一堆邊,詢問邊權在[a,b]的範圍內的能加入的最大邊數使圖無環的情況下使邊權和最小,點1000,邊100000,詢問1000000, 強制在線

把邊先按邊權排序,我們再來思考這個問題。
先想一下簡化版,假設所有詢問的左端點相同,那麼問題是不是就簡單一些了?左端點固定,從這個點開始跑最小生成樹,相當於只選最小生成樹的邊。對於第i條邊,權值爲Ci,如果在最小生成樹上,那麼對答案的貢獻就是Ci,如果不在最小生成樹上,那麼貢獻就是0(因爲肯定不會選這條邊)。然後查詢[a,b]這個區間我們只需要用線段樹求個和就好了。
那如果左端點在動呢?對於第i+1爲左端點我們已經搞定的時候,我們把第i條邊加入到第i+1條邊爲左端點的最優生成樹(或者還未構成一棵樹)中,就有兩種情況,設第i條邊的兩個端點分別爲u,v,第一種就是u,v不連通,那麼直接把第i條邊的權值加進去就好了,第二種就是u,v連通,那麼就把u,v之間最長的一條邊替換即可,再把第i條邊加進去就好了,替換的具體體現就是把其在線段樹上的值改爲0就行了。因爲每次查詢左端點不同,而我們的目標又是要固定左端點,所以要保存所有所有左端點的版本。因此可持久化線段樹即可。
關於如何找到u,v間最長的一條邊,LCT能做到O(logn)的時間複雜度,然而我比較懶。。。寫的暴力O(n)的。。勉強卡過

上面講的都是預處理部分,詢問直接查詢就好了。
時間複雜度O(MlogM+QlogM)(LCT版)
O(MN+QlogM)(暴力版)

    #include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
using namespace std;
const int N = 1005, M = 100005;
typedef long long ll;
template<class T>
inline void read(T &x) {char op;while(((op=getchar())<'0')||(op>'9'));x=op-'0';while(((op=getchar())>='0')&&(op<='9'))(x*=10)+=op-'0'; }
char buf[30];
template<class T> 
inline void out(T x) {
    int i = 0;
    if(!x) putchar('0');
    while(x) buf[++i] = x%10+'0', x/=10;
    while(i) putchar(buf[i--]);
    putchar('\n');
}
int n, m, q, cas, g[N], to[M<<1], idx[M<<1], next[M<<1], w[M<<1], Np, lc[M*50], rc[M*50], root[M], fa[N], pa[N], pid[N], np;
ll sum[M*50];
void push(int x,int y,int id,int c) { next[++Np]=g[x]; to[Np]=y; idx[Np]=id; g[x]=Np; w[Np] = c; }
void Init() {
    Np = 1;np = 0;
    memset(root,0,sizeof(root));
    memset(g,0,sizeof(g));
    memset(sum,0,sizeof(sum));
    memset(lc,0,sizeof(lc));
    memset(rc,0,sizeof(rc));
    for(int i=1;i<=n;i++) fa[i] = i;
}
struct edge { 
    int u,v,c;
    bool operator <(const edge &E)const { return c != E.c ? c < E.c : u < E.u; }
}E[M];
int find(int x) { return x == fa[x] ? x : (fa[x] = find(fa[x])); }
void merge(int x,int y) { fa[find(x)] = find(y); }
bool isconnect(int x,int y) { return find(x) == find(y); }
void pushup(int x) { sum[x] = sum[lc[x]]+sum[rc[x]]; }
void build(int &now,int i,int j) {
    now = ++np;
    if(i == j) {
        sum[now] = 0;
        return;
    }
    int m = (i+j)>>1;
    build(lc[now],i,m);
    build(rc[now],m+1,j);
    pushup(now);
}
inline void copy(int now,int pre) { lc[now] = lc[pre]; rc[now] = rc[pre]; sum[now] = sum[pre]; }
inline void update(int &now,int pre,int i,int j,int x,int v) {
    now = ++np;
    copy(now,pre);
    if(i == j) { sum[now] += v; return; }
    int m = (i+j)>>1;
    if(m>=x) update(lc[now],lc[pre],i,m,x,v);
    else update(rc[now],rc[pre],m+1,j,x,v);
    pushup(now);
}
ll query(int now,int i,int j,int x,int y) {
    if(i>=x && j<=y) return sum[now];
    int m = (i+j)>>1;
    ll tt1 = 0, tt2 = 0;
    if(m>=x) tt1 = query(lc[now],i,m,x,y);
    if(m<y) tt2 = query(rc[now],m+1,j,x,y);
    return tt1+tt2;
}
inline void findedge(int u,int v,int p,int len) {
    queue<int>q;
    memset(pa,0,sizeof(pa));
    pa[u] = -1;
    q.push(u);
    while(!q.empty()) {
        int x = q.front();q.pop();
        for(int i=g[x];i;i=next[i]) {
                int y = to[i];
                if(pa[y]) continue;
                pa[y] = x;
                pid[y] = i;
                q.push(y);
            }
        if(pa[v]) break;
    }

    int id = pid[v], x = v, last1 = pa[v], last2 = v;
    while(x != u) {
        if(w[pid[x]] > w[id]) {
            id = pid[x];
            last1 = pa[x];  
            last2 = x;
        }
        x = pa[x];
    }
    int id2 = id^1;
    if(g[last2] == id2) g[last2] = next[id2];
    for(int i=g[last2];next[i];i=next[i])
        if(next[i] == id2) next[i] = next[next[i]];
    if(g[last1] == id) g[last1] = next[id];
    for(int i=g[last1];next[i];i=next[i]) 
        if(next[i] == id) next[i] = next[next[i]];
    update(root[p],root[p],1,m,idx[id],-w[id]);
}

int main() {
    read(cas);
    while(cas--) {
        read(n);read(m);
        for(int i=1;i<=m;i++) read(E[i].u), read(E[i].v), read(E[i].c);
        sort(E+1,E+1+m);
        Init();
        build(root[m+1],1,m);
        for(int i=m;i>=1;i--) {
            root[i] = ++np;
            update(root[i],root[i+1],1,m,i,E[i].c);
            if(isconnect(E[i].u, E[i].v))
             findedge(E[i].u,E[i].v,i,E[i].c);
            else merge(E[i].u, E[i].v);
            push(E[i].u,E[i].v,i,E[i].c);
            push(E[i].v,E[i].u,i,E[i].c);
        }
        read(q);
        int l, r;
        long long lastans = 0;
        for(int i=1;i<=q;i++) {
            read(l);read(r);
            l -= lastans; r -= lastans;
            l = lower_bound(E+1,E+1+m,(edge){0,0,l})-E;
            r = upper_bound(E+1,E+1+m,(edge){n+1,n+1,r})-E-1;
            if(l > r) lastans = 0;
            else lastans = query(root[l],1,m,l,r);
            out(lastans);
        }
    }
    return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章