洛谷P3247 [HNOI2016]最小公倍数 [分块,并查集]

洛谷


思路

显然,为了达到这个最小公倍数,只能走\(a,b\)不是很大的边。

即,当前询问的是\(A,B\),那么我们只能走\(a\leq A,b\leq B\)的边。

然而,为了达到这最小公倍数,又需要有\(\max\{a\}=A,\max\{b\}=B\)

那么暴力做法就很显然了:并查集维护连通块的\(\max\{a\},\max\{b\}\),询问时把满足条件的边全都连上,看最终是否满足条件。

如何优化呢?

把边按\(a\)排序,撒\(\sqrt m\)个关键点,每个关键点把它前面的边按\(b\)排序。

每个询问找出\(a\leq A\)的最靠右关键点,把这个询问放进去。

每个关键点把询问按\(B\)排序。

在关键点回答询问时维护一个从左到右的指针。由于询问的\(B\)递增,指针可以一直从左往右扫。

然后把这个关键点到下一个关键点之间满足条件的边加进去,得到答案,再撤回。

假设\(n,m,q\)同阶(虽然实际上不同阶),则复杂度\(O(n\sqrt n\log n)​\),可能可以调整块大小得到更优的复杂度。


代码

注意\(a,b\)有0,一开始最大值要赋为-1。

由于使用了较多STL,需要开O2

#include<bits/stdc++.h>
clock_t t=clock();
namespace my_std{
    using namespace std;
    #define pii pair<int,int>
    #define fir first
    #define sec second
    #define MP make_pair
    #define rep(i,x,y) for (int i=(x);i<=(y);i++)
    #define drep(i,x,y) for (int i=(x);i>=(y);i--)
    #define go(x) for (int i=head[x];i;i=edge[i].nxt)
    #define sz 101010
    #define S 350
    typedef long long ll;
    typedef double db;
    mt19937 rng(chrono::steady_clock::now().time_since_epoch().count());
    template<typename T>inline T rnd(T l,T r) {return uniform_int_distribution<T>(l,r)(rng);}
    inline bool chkmax(int &x,int y){return x<y?x=y,1:0;}
    template<typename T>inline void read(T& t)
    {
        t=0;char f=0,ch=getchar();double d=0.1;
        while(ch>'9'||ch<'0') f|=(ch=='-'),ch=getchar();
        while(ch<='9'&&ch>='0') t=t*10+ch-48,ch=getchar();
        if(ch=='.'){ch=getchar();while(ch<='9'&&ch>='0') t+=d*(ch^48),d*=0.1,ch=getchar();}
        t=(f?-t:t);
    }
    template<typename T,typename... Args>inline void read(T& t,Args&... args){read(t); read(args...);}
    void file()
    {
        #ifndef ONLINE_JUDGE
        freopen("a.txt","r",stdin);
        #endif
    }
    inline void chktime()
    {
        #ifndef ONLINE_JUDGE
        cout<<(clock()-t)/1000.0<<'\n';
        #endif
    }
    #ifdef mod
    ll ksm(ll x,int y){ll ret=1;for (;y;y>>=1,x=x*x%mod) if (y&1) ret=ret*x%mod;return ret;}
    ll inv(ll x){return ksm(x,mod-2);}
    #else
    ll ksm(ll x,int y){ll ret=1;for (;y;y>>=1,x=x*x) if (y&1) ret=ret*x;return ret;}
    #endif
//	inline ll mul(ll a,ll b){ll d=(ll)(a*(double)b/mod+0.5);ll ret=a*b-d*mod;if (ret<0) ret+=mod;return ret;}
}
using namespace my_std;

int n,m,Q;
struct hh{int u,v,a,b;}edge[sz];
inline bool cmp1(const hh &x,const hh &y){return x.a<y.a;}
inline bool cmp2(const hh &x,const hh &y){return x.b<y.b;}
bool ans[sz];

struct Query{int u,v,a,b,id;};
inline bool cmp(const Query &x,const Query &y){return x.b<y.b;}
int blo,num,val[S],pos[S];
vector<hh>v[S];
vector<Query>q[S];
void init()
{
    chktime();
    sort(edge+1,edge+m+1,cmp1);
    blo=sqrt(m);num=m/blo+(bool)(m%blo);
    rep(i,1,num) val[i]=edge[pos[i]=i*blo-blo].a;
    chktime();
    rep(i,1,num)
    {
        rep(j,1,pos[i]) v[i].push_back(edge[j]);
        sort(v[i].begin(),v[i].end(),cmp2);
    }
    pos[num+1]=m+1;
}
int fa[sz],mx1[sz],mx2[sz],dep[sz];
int getfa(int x){return x==fa[x]?x:getfa(fa[x]);}
struct hhh{int x,y,mx1,mx2,d;};
hhh merge(hh e)
{
    int x=getfa(e.u),y=getfa(e.v);
    if (dep[x]>dep[y]) swap(x,y);
    hhh ret=(hhh){x,y,mx1[y],mx2[y],dep[y]};
    fa[x]=y;
    chkmax(mx1[y],max(mx1[x],e.a));chkmax(mx2[y],max(mx2[x],e.b));
    if (dep[x]==dep[y]) ++dep[y];
    return ret;
}
inline void del(hhh s){fa[s.x]=s.x;mx1[s.y]=s.mx1;mx2[s.y]=s.mx2;dep[s.y]=s.d;}
void solve(int s)
{
    sort(q[s].begin(),q[s].end(),cmp);
    rep(i,1,n) fa[i]=i,mx1[i]=mx2[i]=-1,dep[i]=1;
    int p=0;
    rep(i,0,(int)q[s].size()-1)
    {
        int aa=q[s][i].a,bb=q[s][i].b;
        while (p<=(int)v[s].size()-1&&v[s][p].b<=bb) merge(v[s][p]),++p;
        stack<hhh>SS;
        rep(j,pos[s]+1,pos[s+1]-1) 
            if (edge[j].a<=aa&&edge[j].b<=bb)
                SS.push(merge(edge[j]));
        int uu=getfa(q[s][i].u),vv=getfa(q[s][i].v);
        if (uu==vv&&mx1[uu]==aa&&mx2[uu]==bb) ans[q[s][i].id]=1;
        while (!SS.empty()) del(SS.top()),SS.pop();
    }
}

int main()
{
    file();
    int x,y,a,b;
    read(n,m);
    rep(i,1,m) read(x,y,a,b),edge[i]=(hh){x,y,a,b};
    chktime();
    read(Q);
    init();
    chktime();
    rep(i,1,Q)
    {
        read(x,y,a,b);
        int t=upper_bound(val+1,val+num+1,a)-val-1;
        q[t].push_back((Query){x,y,a,b,i});
    }
    chktime();
    rep(i,1,num) solve(i);
    rep(i,1,Q) puts(ans[i]?"Yes":"No");
    chktime();
    return 0;
}
posted @ 2019-02-17 14:36  p_b_p_b  阅读(280)  评论(0编辑  收藏  举报