BZOJ4537: [Hnoi2016]最小公倍数

一开始看错题目 以为求的是最大公因数QWQ然后一直看不懂Claris的题解= =
传送门:
http://www.cnblogs.com/clrs97/p/5406018.html
对于边集分块然后每次加上去就好了

好神!

#include<cstdio>
#include<iostream>
#include<cmath>
#include<cstring>
#include<cstdlib>
#include<algorithm>
using namespace std;

char c;
inline void read(int &a)
{a=0;do c=getchar();while(c<'0'||c>'9');while(c<='9'&&c>='0')a=(a<<3)+(a<<1)+c-'0',c=getchar();}

struct Q{int op,a,Old,opp;};//1 Oldf 2 Olda 3 Oldb 4 Oldrank
Q Stack[100001];
int tot;
int f[100001];
int fb[100001],fa[100001],rank[100001];
int find(int x){return x==f[x]?x:find(f[x]);}

inline void Merge(int u,int v,int a,int b)
{
    u=find(u),v=find(v);
    if(u!=v)
    {
      if(rank[u]<rank[v])swap(u,v);
      if(rank[u]==rank[v])Stack[++tot]=(Q){4,a,rank[u],u},rank[u]++;
        Stack[++tot]=(Q){1,a,f[v],v};
       f[v]=u;
    }
    int fl=max(a,fa[v]);
    if(fl>fa[u])
        Stack[++tot]=(Q){2,u,fa[u],u},fa[u]=fl;
    fl=max(b,fb[v]);
    if(fl>fb[u])
        Stack[++tot]=(Q){3,u,fb[u],u},fb[u]=fl;
}

inline void Return(int A)
{
    for(tot;tot&&Stack[tot].a>A;tot--)
    {
        if(Stack[tot].op==1)f[Stack[tot].opp]=Stack[tot].Old;
        if(Stack[tot].op==2)fa[Stack[tot].opp]=Stack[tot].Old;
        if(Stack[tot].op==3)fb[Stack[tot].opp]=Stack[tot].Old;
        if(Stack[tot].op==4)rank[Stack[tot].opp]=Stack[tot].Old;
    }
}

struct Query{int a,b,u,v,no,ans;};
Query Ques[100001];
Query Side[100001];
Query Cache[100001];
int Ans[100001];
inline bool cmpQue(Query a,Query b){return a.a<b.a;}
inline bool cmpQue2(Query a,Query b){return a.b<b.b;}
int n,m;
int cnt;
int main()
{
    read(n),read(m);
    for(int i=1;i<=m;i++)read(Side[i].u),read(Side[i].v),read(Side[i].a),read(Side[i].b);
    int q;
    read(q);
    for(int i=1;i<=q;i++)read(Ques[i].u),read(Ques[i].v),read(Ques[i].a),read(Ques[i].b),Ques[i].no=i;
    int Size=sqrt(m)+1;
    sort(Side+1,Side+1+m,cmpQue);
    for(int i=0;i<=m;i+=Size)
    {
        tot=0;
        cnt=0;
        for(int j=1;j<=q;j++)
            if(Ques[j].a>=Side[i].a&&(i+Size>m||Ques[j].a<Side[i+Size].a))
                Cache[++cnt]=Ques[j];
        if(!cnt)continue;
        sort(Side+1,Side+1+i,cmpQue2);
        sort(Cache+1,Cache+1+cnt,cmpQue2);

        for(int j=1;j<=n;j++)f[j]=j,fa[j]=fb[j]=-1,rank[j]=1;
        for(int j=1,k=1;j<=cnt;j++)
        {
            while(k<=i&&Side[k].b<=Cache[j].b)Merge(Side[k].u,Side[k].v,Side[k].a,Side[k].b),k++;
            tot=0;
            for(int t=i+1;t<=min(i+Size,m);t++)
                if(Side[t].a<=Cache[j].a&&Side[t].b<=Cache[j].b)Merge(Side[t].u,Side[t].v,Side[t].a,Side[t].b);
            int tp;     
            Cache[j].ans=((tp=find(Cache[j].u))==find(Cache[j].v)&&fa[tp]==Cache[j].a&&fb[tp]==Cache[j].b);
            Return(0);
        }
        for(int j=1;j<=cnt;j++)Ans[Cache[j].no]=Cache[j].ans;
    }
    for(int i=1;i<=q;i++)puts(Ans[i]?"Yes":"No");
    return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值