HDU 5333 Undirected Graph

2 篇文章 0 订阅
1 篇文章 0 订阅

题目大意:

对于一个 n(10w) 个点, m(20w) 条边的无向图,有 q(10w) 次询问:只保留编号为 L ~R 的点之间的边,联通块个数是多少。

题解:

开始觉得 L ~R 可以联系到莫队算法,但我对莫队算法理解不深,同时觉得不一定跑得动(立下flag)。
对这道题是否有种似曾相识的感觉呢?
我们曾做过一题叫 【bzoj3514】Codechef MARCH14 GERALD07加强版 。
题目几乎一样,不过每次询问保留的是编号在 L ~R之间的边,并且强制在线。
那题我们被动的预处理 i[L,R] 对答案有贡献当且仅当……
这题我们主动的保留对答案有影响的边。
于是LCT+BIT。

#include<set>
#include<cmath>
#include<queue>
#include<stack>
#include<ctime>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<complex>
#include<iostream>
#include<cassert>
#include<algorithm>
#define pi acos(-1)
#define inf (1<<30)
#define INF (1<<62)
#define CLR(x,f) memset(x,f,sizeof(x))
#define CPY(x,y) memcpy(x,y,sizeof(x))
#define prt(x) cout<<"    "<<#x<<":"<<x<<endl
#define huh(x) printf("--------DEBUG(%d)--------\n",x)
using namespace std;
typedef long long ll;
const int M=100005;

int n,m,q;

struct Edge{
    Edge(){}
    Edge(int u_,int v_):u(u_),v(v_){}
    int u,v;
    bool operator<(const Edge &a)const{
        if(v!=a.v)return v<a.v;
        return u>a.u;
    }
}edge[M<<1];

int fa[M];
int get(int x){
    return fa[x]==x?x:fa[x]=get(fa[x]);
}

struct BIT{
    int C[M];
    void init(){
        memset(C,0,n+1<<2);
    }
    void update(int x,int f){
        for(;x;x-=x&-x)C[x]+=f;
    }
    int query(int x){
        int res=0;
        for(;x<=n;x+=x&-x)res+=C[x];
        return res;
    }
}bit;

struct LCT{
    static const int N=M*3;
    int fa[N],c[N][2],v[N],mn[N];
    char rev[N];
    void init(){
        for(int i=0;i<=n+m;i++){
            c[i][0]=c[i][1]=fa[i]=0;
            v[i]=inf;mn[i]=i;
        }
    }
    bool isroot(int x){
        return c[fa[x]][0]!=x&&c[fa[x]][1]!=x;
    }
    int cmp(int x,int y){
        return v[x]<v[y]?x:y;
    }
    void up(int x){
        mn[x]=cmp(cmp(mn[c[x][0]],mn[c[x][1]]),x);
    }
    int query(int x,int y){
        makeroot(x);access(y);splay(y);
        return mn[y];
    }
    void rotate(int x){
        int y=fa[x],z=fa[y],l,r;
        if(c[y][0]==x)l=0;else l=1;r=l^1;
        if(!isroot(y)){
            if(c[z][0]==y)c[z][0]=x;
            else c[z][1]=x;
        }
        fa[x]=z;fa[y]=x;fa[c[x][r]]=y;
        c[y][l]=c[x][r];c[x][r]=y;
        up(y);
    }
    int q[N];
    void down(int x){
        if(rev[x]){
            rev[c[x][0]]^=1;
            rev[c[x][1]]^=1;
            swap(c[x][0],c[x][1]);
            rev[x]=0;
        }
    }
    void splay(int x){
        int top=0;q[++top]=x;
        for(int i=x;!isroot(i);i=fa[i])q[++top]=fa[i];
        for(int i=top;i>=1;i--)down(q[i]);
        while(!isroot(x)){
            int y=fa[x],z=fa[y];
            if(!isroot(y)){
                if(c[z][0]==y^c[y][0]==x)rotate(x);
                else rotate(y);
            }
            rotate(x);
        }up(x);
    }
    void makeroot(int x){
        access(x);splay(x);rev[x]^=1;
    }
    void access(int x){
        for(int t=0;x;t=x,x=fa[x]){
            splay(x);c[x][1]=t;up(x);
        }
    }
    void cut(int x,int y){
        makeroot(x);access(y);splay(y);
        c[y][0]=fa[x]=0;
    }
    void link(int x,int y){
        makeroot(x);fa[x]=y;
    }
}lct;

int last[M],allc;
struct Query{
    int to,l,nxt;
}que[M];

void ins(int l,int r,int id){
    que[allc].l=l;
    que[allc].to=id;
    que[allc].nxt=last[r];
    last[r]=allc++;
}

int ans[M];

void solve(){
    int tmp=0;
    for(int u,v,i=1;i<=m;i++){
        scanf("%d%d",&u,&v);
        if(u==v)continue;
        if(u>v)swap(u,v);
        edge[++tmp]=Edge(u,v);
    }
    m=tmp;
    sort(edge+1,edge+m+1);
    for(int i=1;i<=n;i++)fa[i]=i;
    bit.init();lct.init();
    memset(last,-1,n+1<<2);allc=0;
    for(int l,r,i=1;i<=q;i++){
        scanf("%d%d",&l,&r);
        ins(l,r,i);
    }
    int pre=1;
    for(int i=1;i<=n;i++){
        for(;pre<=m&&edge[pre].v==i;pre++){
            int u=edge[pre].u,v=edge[pre].v;
            if(get(u)==get(v)){
                int id=lct.query(u,v);id-=n;
                if(edge[id].u>=u)continue;
                lct.cut(edge[id].u,id+n);
                lct.cut(edge[id].v,id+n);
                bit.update(edge[id].u,-1);
                lct.v[pre+n]=u;
                lct.link(u,pre+n);
                lct.link(v,pre+n);
                bit.update(u,1);
            }
            else{
                lct.v[pre+n]=u;
                lct.link(u,pre+n);
                lct.link(v,pre+n);
                bit.update(u,1);
                fa[get(u)]=get(v);
            }
        }
        for(int j=last[i];j!=-1;j=que[j].nxt){
            int l=que[j].l,to=que[j].to;
            ans[to]=n-bit.query(l);
        }
    }
    for(int i=1;i<=q;i++)
        printf("%d\n",ans[i]);
}

int main(){
    while(~scanf("%d%d%d",&n,&m,&q))solve();
    return 0;
}

提一下和官方题解不一样的官方标程做法:莫队+dsu。

以下是我研读之后的代码。

#include<set>
#include<cmath>
#include<queue>
#include<stack>
#include<cstdio>
#include<bitset>
#include<cassert>
#include<cstring>
#include<complex>
#include<iostream>
#include<algorithm>
#define pi acos(-1)
#define inf (1<<30)
#define INF (1<<62)
#define y1 bflaisfnmasf
#define y2 fsafgmalg
#define tm afnsjkf
#define j1 sfakf
#define j2 fasndfkas
#define fi first
#define se second
#define CLR(x,f) memset(x,f,sizeof(x))
#define CPY(x,y) memcpy(x,y,sizeof(x))
#define prt(x) cout<<#x<<":"<<x<<" "
#define prtn(x) cout<<#x<<":"<<x<<endl
#define huh(x) printf("--------case(%d)--------\n",x)
#pragma comment(linker, "/STACK:1024000000,1024000000")
using namespace std;
typedef long long ll;
typedef pair<int,int> ii;
const int M=100005;
int n,m,Q;

int allc,last[M];
ll deg[M];

struct Edge{
    Edge(){}
    Edge(int u_,int v_):to(u_),nxt(v_){}
    int to,nxt;
}edge[M<<2];
void ins(int u,int v){
    deg[u]++;
    edge[allc]=Edge(v,last[u]);
    last[u]=allc++;
}


int ans;

int fa[M],di[M],tcnt;
ii tar[M<<1]; 
int getFa(int x){
    while(fa[x]!=x)x=fa[x];return x;
}

void addin(int l,int r,int lim){

    int tcnt=0;
    for(int i=l;i<=r;i++){
        for(int j=last[i];j!=-1;j=edge[j].nxt){
            int to=edge[j].to;
            if(to<=i||to>lim)continue;
            int x=getFa(i),y=getFa(to);
            if(x!=y){
                ans--;
                if(di[x]<di[y]){
                    tar[++tcnt]=ii(x,di[x]);
                    fa[x]=y;
                }
                else{
                    tar[++tcnt]=ii(y,di[y]);
                    fa[y]=x;
                    if(di[x]==di[y]){
                        tar[++tcnt]=ii(x,di[x]);
                        di[x]++;
                    }
                }
            }
        }
    }

    for(;tcnt;tcnt--){
        fa[tar[tcnt].fi]=tar[tcnt].fi;
        di[tar[tcnt].fi]=tar[tcnt].se;
    }
}

int bel[M];
struct ques{
    int l,r,id;
    inline bool operator<(const ques &a)const{
        if(bel[l]!=bel[a.l])return bel[l]<bel[a.l];
        return r<a.r;
    }
}q[M];
int res[M];

int R[M],tot;

void rd(int &r){
    r=0;char c;
    while(c=getchar(),c<48);
    do r=r*10+(c^48);
    while(c=getchar(),c>47);
}
int kase;
void solve(){
//  huh(++kase);
    allc=0;for(int i=1;i<=n;i++)deg[i]=0,last[i]=-1;
    for(int u,v,i=1;i<=m;i++){
        rd(u);rd(v);
        ins(u,v);ins(v,u);
    }
    for(int i=1;i<=Q;i++){
        rd(q[i].l);rd(q[i].r);q[i].id=i;
    }

    tot=0;
    for(int i=1;i<=n;i++)deg[i]+=deg[i-1];
    int lim;if(n<=1000&&m<=1000)lim=30;else lim=800;//:P

    while(R[tot]<n){
        tot++;R[tot]=R[tot-1]+1;
        while(R[tot]<=n&&deg[R[tot]]-deg[R[tot-1]]<=lim&&R[tot]-R[tot-1]<=lim)R[tot]++;
        R[tot]=max(R[tot]-1,R[tot-1]+1);
    }
    for(int i=1;i<=tot;i++)
        for(int j=R[i-1]+1;j<=R[i];j++)
            bel[j]=i;

    sort(q+1,q+Q+1);

    for(int now=1,i=1;now<=Q&&i<=tot;i++){//ith block
        if(bel[q[now].l]>i)continue;
        for(int j=1;j<=n;j++)fa[j]=j,di[j]=1;
        ans=n;
        while(now<=Q&&bel[q[now].r]==i){
            addin(q[now].l,q[now].r,q[now].r);
            res[q[now].id]=ans;ans=n;now++;
        }
        for(int j=R[i]+1;bel[q[now].l]==i&&j<=n;j++){
            for(int k=last[j];k!=-1;k=edge[k].nxt){
                int to=edge[k].to;
                if(to<=R[i]||to>j)continue;
                int x=getFa(j),y=getFa(to);
                if(x!=y){
                    ans--;
                    if(di[x]<di[y])fa[x]=y;
                    else{
                        fa[y]=x;
                        if(di[x]==di[y])di[x]++;
                    }
                }
            }
            int preans=ans;
            while(now<=Q&&bel[q[now].l]==i&&q[now].r==j){
                addin(q[now].l,R[i],q[now].r);res[q[now].id]=ans;
                ans=preans;now++;
            }
        }
    }

    for(int i=1;i<=Q;i++)
        printf("%d\n",res[i]);
}
int main(){
    freopen("1007.in","r",stdin);
    freopen("1007.ans","w",stdout);
    while(~scanf("%d%d%d",&n,&m,&Q))solve();
    return 0;
}

再回顾标程,发现自己对于小细节上的优化没有以前敏感了,枚举边的时候固定的那一端不用每次都getFa(),合并的时候顺便更新一下即可(如标程)。
依然对标程用 q[now].l1 判定表示不太理解。
最后超时真是不知所措,改成和标程一模一样就没意思了,所以先就这样吧。<( ̄︶ ̄)>

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值