• ZOJ4100 Vertices in the Pocket(并查集+权值线段树+二分)


    #include<bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    const int maxn=1e6+14;
    ll father[maxn];
    ll num[maxn];
    ll N,Q;
    ll block;
    ll ans;//表示在当前的图下,不增加连通块数量所能增加的边数 
    
    //线段树部分
    struct node {
        ll l,r;
        ll cnt;//大小为这样的连通块的数量
        ll sum;//连通块的总的点数
        ll pfh;//各连通块之间点数的平方和 
    }segTree[maxn*4]; 
    void pushup (ll i) {
        segTree[i].cnt=segTree[i<<1].cnt+segTree[i<<1|1].cnt;
        segTree[i].sum=segTree[i<<1].sum+segTree[i<<1|1].sum;
        segTree[i].pfh=segTree[i<<1].pfh+segTree[i<<1|1].pfh; 
    }
    void build (ll i,ll l,ll r) {
        segTree[i].l=l;
        segTree[i].r=r;
        if (l==r) {
            segTree[i].pfh=l==1?N:0;
            segTree[i].sum=l==1?N:0;
            segTree[i].cnt=l==1?N:0;
            return;
        }
        ll mid=(l+r)>>1;
        build(i<<1,l,mid);
        build(i<<1|1,mid+1,r);
        pushup(i); 
    }
    void update (ll i,ll t,ll b) {
        if (segTree[i].l==segTree[i].r) {
            segTree[i].cnt+=b;
            segTree[i].sum+=b*t;
            segTree[i].pfh+=b*t*t;
            return;
        }
        ll mid=(segTree[i].l+segTree[i].r)>>1;
        if (t<=mid) update(i<<1,t,b);
        else update(i<<1|1,t,b);
        pushup(i);
    }
    ll ask (ll i,ll k,ll sum) {
        if (segTree[i].l==segTree[i].r) {
            ll l=1;
            ll r=segTree[i].cnt;
            ll mid=(l+r)>>1;
            while (l<r) {
                mid=(l+r)>>1;
                ll tmp=mid*(mid-1)/2*segTree[i].l*segTree[i].l+mid*segTree[i].l*sum;
                if (tmp>=k) r=mid;
                else l=mid+1;
            }
            return l;
        }
        ll mid=(segTree[i].l+segTree[i].r)>>1;
        ll tmp=(segTree[i<<1|1].sum*segTree[i<<1|1].sum-segTree[i<<1|1].pfh)/2+segTree[i<<1|1].sum*sum;
        if (tmp<k)
            return segTree[i<<1|1].cnt+ask(i<<1,k-tmp,sum+segTree[i<<1|1].sum);
        else return ask(i<<1|1,k,sum);
    }
    void init () {
        for (int i=1;i<=N;i++) {
            num[i]=1;
            father[i]=i;
        }
        block=N;
        build(1,1,N);
        ans=0;
    }
    
    ll findfather (ll x) {
        ll a=x;
        while (x!=father[x]) x=father[x];
        while (a!=father[a]) {
            ll z=a;
            a=father[a];
            father[z]=x;
        }
        return x;
    }
    
    void Union (int a,int b) {
        ll faA=findfather(a);
        ll faB=findfather(b);
        if (faA==faB) {
            ans--;
            return;
        }
        if (faA>faB) swap(faA,faB);
        update(1,num[faA],-1);
        update(1,num[faB],-1);
        
        father[faA]=faB;
        block--;
        ans+=num[faB]*num[faA]-1;
        num[faB]+=num[faA];
        num[faA]=0;
        update(1,num[faB],1);
    }
    
    ll getMin (ll k) {
        return max(1LL,block-k); 
    }
    
    ll getMax (ll k) {
        if (k<=ans) return block;
        k-=ans;
        ll ck=ask(1,k,0);
        return block-ck+1;
    }
    
    int main () {
        int T;
        scanf("%d",&T);
        while (T--) {
            scanf("%lld %lld",&N,&Q);
            init(); 
            ll a,b;
            ll query;
            while (Q--) {
                scanf("%lld",&query);
                if (query==1) {
                    scanf("%lld %lld",&a,&b);
                    Union(a,b);
                }
                else {
                   scanf("%lld",&a);
                   printf("%lld %lld
    ",getMin(a),getMax(a));
                }
            }
        }
        return 0;
    }
  • 相关阅读:
    AOP的实现原理——动态代理
    反射
    代理模式
    sprig——jar包
    spring事务管理
    [c++基础]3/5原则--拷贝构造函数+拷贝赋值操作符
    [c++面试准备]--vector对象是如何增长的
    c++面试须知
    redhat--1
    kvm配置虚拟机[待整理]
  • 原文地址:https://www.cnblogs.com/zhanglichen/p/12442977.html
Copyright © 2020-2023  润新知