BZOJ4537: [Hnoi2016]最小公倍数(分块)

传送门

题解:

好暴力的一道题啊。。

容易发现如果把所有边看做二维平面上的点,那么我们要查询某个左下方的矩形的连通性以及最大值。

这个用数据结构不好维护,用稍微暴力一点的分块来维护。

按照横坐标 a a 分为n块,询问放到对应块并按照 b b 的顺序来对于前面的块做扫描线,每个块会被扫描m次。

还要考虑本块的贡献,这个可以直接暴力扫一遍,因为只有 m m 条,然后用个支持撤销的按秩合并的并查集维护一下就行了,时间复杂度 O(mmlogn) O ( m m log ⁡ n )

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

inline int rd() {
    char ch=getchar(); int i=0,f=1;
    while(!isdigit(ch)) {if(ch=='-')f=-1; ch=getchar();}
    while(isdigit(ch)) {i=(i<<1)+(i<<3)+ch-'0'; ch=getchar();}
    return i*f;
}
const int N=5e4+50,M=1e5+50,B=1e3+50;
int n,m,q,bl,h,ans[N],tot,bg[B],ed[B],pos[B];
struct data {
    int u,v,a,b;
    data() {}
    data(int u,int v,int a,int b):u(u),v(v),a(a),b(b){}
    friend inline bool operator <(const data &a,const data &b) {
        if(a.a!=b.a) return a.a<b.a;
        return a.b<b.b;
    }
} op[M];
inline bool cmp(const data &a,const data &b) {
    if(a.b!=b.b) return a.b<b.b;
    return a.a<b.a;
}
struct ask{
    int id,u,v,a,b;
    friend inline bool operator <(const ask &a,const ask &b) {
        if(a.b!=b.b) return a.b<b.b;
        return a.a<b.a;
    }
};
vector <ask> qry[B];
struct type{int f,a,b;};
int mxa[N],mxb[N],anc[N],rk[N];
inline type getanc(int i) {
    if(i==anc[i]) return (type){i,mxa[i],mxb[i]};
    return getanc(anc[i]);
}
struct type2 {int u,prea,preb,x,y;};
vector <type2> st;
inline type2 link(int x,int y,int a,int b) {
    x=getanc(x).f, y=getanc(y).f;
    type2 tp; 
    if(x==y) {
        tp.u=0; tp.x=x;
    } else {
        if(rk[x]<rk[y]) swap(x,y);
        tp.u=1+(rk[x]==rk[y]);
        rk[x]+=(rk[x]==rk[y]);
        tp.x=x; tp.y=y; anc[y]=x;
    }
    tp.prea=mxa[x]; tp.preb=mxb[x];
    mxa[x]=max(mxa[x],max(a,mxa[y]));
    mxb[x]=max(mxb[x],max(b,mxb[y]));
    return tp;
}
inline void cut(type2 tp) {
    if(tp.u) {
        anc[tp.y]=tp.y; rk[tp.x]-=(tp.u-1);
    }
    mxa[tp.x]=tp.prea;
    mxb[tp.x]=tp.preb;
}
int main() {
    n=rd(), m=rd();
    for(int i=1;i<=m;i++) op[i].u=rd(), op[i].v=rd(), op[i].a=rd(), op[i].b=rd();
    sort(op+1,op+m+1);
    bl=sqrt(m); h=(m-1)/bl+1;
    for(int i=1;i<=h;i++) 
        bg[i]=(i-1)*bl+1, ed[i]=min(i*bl,m);
    q=rd();
    for(int i=1;i<=q;i++) {
        int u=rd(), v=rd(), a=rd(), b=rd();
        for(int j=h;j>=1;j--) {
            if(op[ed[j]].a>=a && (op[ed[j-1]].a<=a || j==1)) {
                qry[j].push_back((ask){i,u,v,a,b});
                break;
            }
        }
    }
    for(int i=1;i<=h;i++) if(qry[i].size()) sort(qry[i].begin(),qry[i].end());
    for(int i=1;i<=h;i++) sort(op+bg[i],op+ed[i]+1,cmp);
    for(int i=1;i<=n;i++) anc[i]=i, mxa[i]=-1, mxb[i]=-1;
    for(int i=1;i<=h;i++) {
        for(int j=1;j<=n;j++) anc[j]=j, mxa[j]=-1, mxb[j]=-1;
        for(int j=1;j<i;j++) pos[j]=bg[j];
        for(int j=0;j<qry[i].size();++j) {
            ask t=qry[i][j];
            st.clear();
            for(int p=1;p<i;p++) 
                for(;pos[p]<=ed[p] && op[pos[p]].b<=t.b;++pos[p])
                    link(op[pos[p]].u,op[pos[p]].v,op[pos[p]].a,op[pos[p]].b);
            for(int p=bg[i];p<=ed[i] && op[p].b<=t.b; ++p) 
                if(op[p].a<=t.a) st.push_back(link(op[p].u,op[p].v,op[p].a,op[p].b));
            type f1=getanc(t.u), f2=getanc(t.v);
            if(f1.f==f2.f && f1.a==t.a && f1.b==t.b) ans[t.id]=1;
            for(int p=st.size()-1;p>=0;p--) cut(st[p]);
        }
    }
    for(int i=1;i<=q;i++) ans[i]?(puts("Yes")):(puts("No"));
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值