题解-APIO2019桥梁

problem

\(\mathrm {loj-3145}\)

题意概要:给定一张 \(n\)\(m\) 边的无向图,边有边权,共 \(q\) 次操作,每次会将第 \(x\) 条边的权值改为 \(y\),或询问从 \(x\) 开始只走大于等于 \(y\) 的边能到达多少点。

\(n\leq 5\times 10^4,\ m,q\leq 10^5\)

Solution

这道题和 \(HNOI2016\) 最小公因数 长得很像,想到分块就会了。由于这题有修改,对询问的权值分块不好做,就只能对操作分块了。

设操作块大小为 \(T\),则共 \(\lceil\frac qT \rceil\) 个操作快。

对于每一个操作块,按照询问的权值大小顺序询问(否则就需要上可持久化并查集)。需要考虑两者的贡献:在块内被修改了的边、在块内未被修改的边。后者可以依照询问的权值大小依次添加,而由于目前将询问按权值大小排序,所以询问时间不一定递增,可以暴力处理块内修改,查看这条边是否有用。

对于每一个询问,需要暴力扫描块内的修改,每次修改因为需要支持并查集撤销,复杂度 \(O(T\log n)\)。总共 \(q\) 个询问,共 \(O(qT\log n)\)

对于每一块而言,需要将所有边排序、块内询问排序,处理整块排序时会重构整个并查集,复杂度 \(O(n+m\log m+T\log T)\),由于 \(n,m,q\) 同阶,所以简化为 \(O(m\log m)\)。总共 \(\lceil \frac qT\rceil\) 块,共 \(O(\frac qT\cdot m\log m)\)

考虑到 \(n,m,q\) 同阶,总复杂度为 \(O(mT\log m+\frac {m^2\log m}T)=O(m\log m(T+\frac mT))\),取 \(T=m^{\frac 12}\) 得复杂度最低为 \(O(m^{\frac 32}\log m)\)。到这就能过了

实际上没必要每一块内重新排序,可以将一开始排序后的序列拆出来,复杂度 \(O(m)\),重新计算复杂度得 \(O(mT\log m+\frac {m^2}T)\),取 \(T=\sqrt {m\log m}\) 可得 \(O(m^{\frac 32}\sqrt {\log m})\)……但效率差别并不是很大。

Code

这里写的是前一个版本的。

//loj-3145
#include <bits/stdc++.h>
using namespace std;

template <typename _tp> inline void read(_tp&x){
    char ch=getchar(),ob=0;x=0;
    while(ch!='-'&&!isdigit(ch))ch=getchar();if(ch=='-')ob=1,ch=getchar();
    while(isdigit(ch))x=x*10+ch-'0',ch=getchar();if(ob)x=-x;
}

const int N = 101000;
int n, m, Q;

struct Edge {
    int l, r, w, id;
    inline void in() {read(l), read(r), read(w);}
    friend inline bool operator < (const Edge&A, const Edge&B) {return A.w > B.w;}
}e[N], ee[N];

struct Qry {
    int op, x, y, id;
    inline void in() {read(op), read(x), read(y);}
    friend inline bool operator < (const Qry&A, const Qry&B) {return A.y > B.y;}
}q[N], md[N], qr[N];

int d[N], sz[N];
inline int f(int x) {while(d[x]) x = d[x]; return x;}

struct RUB {int x, dx, y, sy;}Rub[N];
int rub;

inline void merge(int x, int y) {
    x = f(x), y = f(y); if(x == y) return ;
    if(sz[x] > sz[y]) swap(x, y);
    Rub[++rub] = (RUB) {x, d[x], y, sz[y]};
    d[x] = y, sz[y] += sz[x];
}

inline void cancel() {while(rub) d[Rub[rub].x] = Rub[rub].dx, sz[Rub[rub].y] = Rub[rub].sy, --rub;}

int Ans[N], st[N], ew[N];
bool ex[N];

int main() {
    read(n), read(m);
    for(int i=1;i<=m;++i) e[i].in(), e[i].id = i;
    int T = max(1.0, sqrt(m*log(m)/log(2)));
    read(Q);
    for(int l=1,r=T;l<=Q;l+=T) {
        r = min(Q, l+T-1);
        int t = r - l + 1, t0 = 0, t1 = 0;

        for(int i=1;i<=m;++i) ex[i] = false;

        for(int i=1;i<=t;++i) {
            Ans[i] = 0, q[i].in(), q[i].id = i;
            if(q[i].op == 1) md[++t0] = q[i], ex[q[i].x] = true;
            else qr[++t1] = q[i];
        }
        sort(qr+1, qr+t1+1);
        
        for(int i=1;i<=n;++i) d[i] = 0, sz[i] = 1;

        int em = 0, tp = 0;
        for(int i=1;i<=m;++i)
            if(!ex[i]) ee[++em] = e[i];
            else st[++tp] = i;
        sort(ee+1, ee+em+1);

        int ie = 1;
        for(int i=1;i<=t1;++i) {
            while(ie <= em and ee[ie].w >= qr[i].y)
                merge(ee[ie].l, ee[ie].r), ++ie;
            
            for(int j=1;j<=tp;++j) ew[st[j]] = e[st[j]].w;
            for(int j=1;j<=t0 and md[j].id <= qr[i].id;++j)
                ew[md[j].x] = md[j].y;
            rub = 0;
            for(int j=1;j<=tp;++j)
                if(ew[st[j]] >= qr[i].y)
                    merge(e[st[j]].l, e[st[j]].r);
            Ans[qr[i].id] = sz[f(qr[i].x)];
            cancel();
        }
        for(int i=1;i<=t0;++i)
            e[md[i].x].w = md[i].y;
        for(int i=1;i<=t;++i)
            if(Ans[i]) printf("%d\n", Ans[i]);
    }
    return 0;
}

转载于:https://www.cnblogs.com/penth/p/11382677.html

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值