LOJ#3145. 「APIO2019」桥梁 分块+可撤销并查集

看到这道题时没有什么思路,只会打暴力,而且数据范围比较有迷惑性,那基本就是分块了.
现在有两个暴力:
1.每次 $O(1)$ 更新边权,然后 $O(m)$ 暴力查询一个点的答案.
2.每次将所有边排序,然后 $O(1)/O(\log n)$ 查询点权
上述两种做法中查询与更新的时间复杂度很不平衡,所以考虑对操作进行分块来维持平衡.
令每一个块的大小为 $B$,将 $B$ 中所有询问按照限制重量由大到小排序.
对于所有未出现在 $B$ 中的边也按照边权从大到小排序.
假设 $B$ 中没有修改的话我们可以在 $O(\frac{m}{B} m)$ 的复杂度解决该问题.
而 $B$ 中的修改我们就暴力更新就行,平均下来一个询问要修改 $O(B)$ 次.
那么时间复杂度就是 $O(\frac{m}{B}m \log m+Q B \log m)$.
这个 $B$ 取大一点效果比较好,这里取到了 $700.$

#include <cstdio>
#include <stack>
#include <vector>
#include <cstring>
#include <algorithm>  
#define N 100004    
#define M 200009 
#define ll long long 
#define pb push_back  
#define setIO(s) freopen(s".in","r",stdin) 
using namespace std;
const int B=800;  
int n,m,Q;     
int id[N],ans[M],mark[M],vis[M];      
struct Edge { 
    int u,v,c;  
    Edge(int u=0,int v=0,int c=0):u(u),v(v),c(c){}      
    bool operator<(const Edge b) const { 
        return c>b.c;  
    }
}e[M],f[M];    
struct OPT { 
    int ty,x,y,ti;   
    OPT(int ty=0,int x=0,int y=0,int ti=0):ty(ty),x(x),y(y),ti(ti){}     
    // 优先处理限重更大的   
    bool operator<(const OPT b) const { 
        return y>b.y;  
    }
}a[M],g[M];   
struct UFS {       
    int size[N],p[N];    
    void init() { 
        for(int i=1;i<N;++i) { 
            size[i]=1,p[i]=i;  
        }
    }     
    int find(int x) {   
        return p[x]==x?x:find(p[x]);   
    }
    void ADD(int x,int y) {      
        x=find(x),y=find(y);  
        // 合并 x 与 y     
        if(x==y) {  
            return;  
        }
        if(size[x]>size[y]) {   
            swap(x,y); 
        } 
        // 将 x 合并到 y 上      
        size[y]+=size[x],p[x]=y;     
    }   
    struct DEL {    
        int x,y;  
        DEL(int x=0,int y=0):x(x),y(y){}  
    };  
    stack<DEL>v;   
    void TEM(int x,int y) { 
        x=find(x),y=find(y);      
        if(x==y) return; 
        if(size[x]>size[y]) {   
            swap(x,y); 
        } 
        size[y]+=size[x],p[x]=y;  
        v.push(DEL(x,y));  
    }       
    int Query(int x) {  
        x=find(x); 
        return size[x];  
    }
    void CLR() {        
        while(!v.empty()) {  
            DEL cur=v.top(); v.pop();  
            size[cur.y]-=size[cur.x];         
            p[cur.x]=cur.x;  
        }
    }   
}T;   
void calc(int cur) { 
    // 当前位置为 cur  
    int cnt=0,c2=0;
    for(int i=cur;i<=Q;++i) {   
        if(id[i]!=id[cur]) break;   
        if(a[i].ty==1) mark[a[i].x]=cur;       
        else g[++c2]=a[i],g[c2].ti=i;      
    }   
    for(int i=1;i<=m;++i) { 
    	vis[i]=0;    
        if(mark[i]==cur) continue;   
        f[++cnt]=e[i];    
    }    
    sort(g+1,g+1+c2); 
    sort(f+1,f+1+cnt);  
    T.init(); 
    for(int i=1,j=1;i<=c2;++i) {   
        while(j<=cnt&&f[j].c>=g[i].y) {    
            T.ADD(f[j].u,f[j].v);  
            ++j;  
        }
        for(int k=cur;k<=Q;++k) { 
            if(id[k]!=id[cur]) break;  
            if(a[k].ty==1) vis[a[k].x]=0;   
        }
        for(int k=g[i].ti-1;k>=cur;--k) {   
            if(a[k].ty==1&&!vis[a[k].x]&&a[k].y>=g[i].y) {    
                vis[a[k].x]=1;  
                T.TEM(e[a[k].x].u,e[a[k].x].v);  
            }   
            if(a[k].ty==1) vis[a[k].x]=1;       
        }
        for(int k=g[i].ti+1;k<=Q;++k) {
        	if(id[k]!=id[cur]) break;    
        	if(a[k].ty==1&&!vis[a[k].x]&&e[a[k].x].c>=g[i].y) {
        		T.TEM(e[a[k].x].u,e[a[k].x].v);   
        	}
        }
        // 处理完贡献了,可以查询   
        ans[g[i].ti]=T.Query(g[i].x);    
        T.CLR();   
    }   
    for(int i=cur;i<=Q;++i) { 
        if(id[i]!=id[cur]) break;   
        if(a[i].ty==1) {    
            e[a[i].x].c=a[i].y;    
        }    
    }    
}
int main() { 
    // setIO("input");  
    int x,y,z;     
    scanf("%d%d",&n,&m);  
    for(int i=1;i<=m;++i) { 
        scanf("%d%d%d",&e[i].u,&e[i].v,&e[i].c);   
    }   
    scanf("%d",&Q);    
    // ty=1, w(x)->y    
    // ty=2, s(x)->y        
    for(int i=1;i<=Q;++i) { 
        id[i]=(i-1)/B+1;            
        scanf("%d%d%d",&a[i].ty,&a[i].x,&a[i].y);            
    }        
    for(int i=1;i<=Q;++i) { 
        if(id[i]!=id[i-1]) {   
            calc(i); 
        }  
    }
    for(int i=1;i<=Q;++i) {     
        if(a[i].ty==2) printf("%d\n",ans[i]);  
    }   
    return 0; 
}

  

posted @ 2020-08-12 20:30  EM-LGH  阅读(147)  评论(0编辑  收藏  举报