BZOJ 4537 [Hnoi2016]最小公倍数

并查集+分块或KD树分治

暴力的做法是把所有两维边权不大于询问的边用并查集做一下,查询两点是否连通以及连通块内有没有询问的值。
这是二维关系,显然我们会想到对第一维排序,动态地维护第二维。如果每一次都重建一下是不科学的,我们要维护图的连通性或者减少重建。二维关系不资磁线段树分治,所以不太好维护图的连通性,考虑减少重建次数,那就分块重建……把边分成根号块,每一次处理第一维在块内的询问。对于块前的边按第二维单调扫,块内暴力查。

另一种少见的做法(黑科技)。orz CJK。上面说到这是二维关系,一条边能贡献所有两维都大于它的询问。线段树做不了,那就……KD树分治!时间复杂度也是 O(nn logn) 。所以说修改对询问有贡献,且修改和询问都有偏序关系,一般可以考虑用线段树,二维情况下使用KD树,在数据随机的情况下还是很不错的。

以下分块代码

#include<cmath>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define N 100005
#define cmax(u,v) ((u)<(v)?(u)=(v):0)
using namespace std;
namespace runzhe2000
{
    int n, m, S, qcnt, fa[N], fva[N], fvb[N], stacnt, dep[N], ans[N];
    struct stac{int x, d, a, b;}sta[N];
    struct edge
    {
        int u, v, a, b, id;
        bool operator < (const edge &that) const {return a == that.a ? b < that.b : a < that.a;}
    }e[N], q[N], p[N];
    bool cmp_b(edge u, edge v){return u.b < v.b;}
    int find(int x){return fa[x] == x ? x : find(fa[x]);}
    void merge(int a, int b, int va, int vb)
    {
        a = find(a), b = find(b); 
        if(a == b) 
        {
            sta[++stacnt] = (stac){a, dep[a], fva[a], fvb[a]};
            cmax(fva[a], va); cmax(fvb[a], vb);
            return;
        } 
        if(dep[a] < dep[b]) fa[a] = b, sta[++stacnt] = (stac){a, dep[b], fva[b], fvb[b]}, cmax(fva[b], max(va,fva[a])), cmax(fvb[b], max(vb,fvb[a]));
        else if(dep[a] > dep[b]) fa[b] = a, sta[++stacnt] = (stac){b, dep[a], fva[a], fvb[a]}, cmax(fva[a], max(va,fva[b])), cmax(fvb[a], max(vb,fvb[b]));
        else ++dep[a], fa[b] = a, sta[++stacnt] = (stac){b, dep[a] - 1, fva[a], fvb[a]}, cmax(fva[a], max(va,fva[b])), cmax(fvb[a], max(vb,fvb[b]));
    }
    void recover(){int x = sta[stacnt].x; fva[fa[x]] = sta[stacnt].a, fvb[fa[x]] = sta[stacnt].b; dep[fa[x]] = sta[stacnt].d; fa[x] = x; --stacnt;}
    void main()
    {
        scanf("%d%d",&n,&m); for(int i = 1; i <= m; i++) scanf("%d%d%d%d",&e[i].u,&e[i].v,&e[i].a,&e[i].b);
        scanf("%d",&qcnt); for(int i = 1; i <= qcnt; i++) scanf("%d%d%d%d",&q[i].u,&q[i].v,&q[i].a,&q[i].b), q[i].id = i;
        for(int i = 1; i <= n; i++) fa[i] = i, fva[i] = -1, fvb[i] = -1; S = sqrt(m*20);
        sort(e+1,e+1+m); sort(q+1,q+1+qcnt); int curq = 1, curp = 0, pcnt = 0;
        for(int i = 1; i <= m; i += S)
        {
            int beg = curq, end = curq - 1; curp = 0;
            for(int j = curq; j <= qcnt; j++) if(q[j].a < e[i + S].a || i + S > m) ++end; else break;
            sort(q+beg, q+end+1, cmp_b); for(; stacnt; ) recover();
            for(; curq <= end; curq++)
            {
                for(; p[curp+1].b <= q[curq].b && curp < pcnt; curp++)
                    merge(p[curp+1].u, p[curp+1].v, p[curp+1].a, p[curp+1].b);
                int tstacnt = stacnt;
                for(int j = i, jj = min(m + 1, i + S); j < jj; j++) if(e[j].a <= q[curq].a && e[j].b <= q[curq].b)
                    merge(e[j].u, e[j].v, e[j].a, e[j].b);

                int tmp = find(q[curq].u);              
                if(tmp != find(q[curq].v)) ans[q[curq].id] = 0;
                else ans[q[curq].id] = fva[tmp] == q[curq].a && fvb[tmp] == q[curq].b;
                for(; tstacnt != stacnt; ) recover();
            }
            for(int j = i, jj = min(m + 1, i + S); j < jj; j++) p[++pcnt] = e[j]; sort(p+1, p+1+pcnt, cmp_b);
        }
        for(int i = 1; i <= qcnt; i++) puts(ans[i] ? "Yes" : "No");
    }
}
int main()
{
    runzhe2000::main();
}

以下KD树分治代码

#include<cstdio>
#include<algorithm>
#define N 100005
#define cmax(u,v) ((u)<(v)?(u)=(v):0)
using namespace std;
namespace runzhe2000
{
    int n, m, qcnt, ans[N], dep[N], f[N], fva[N], fvb[N], stacnt, D;
    struct edge
    {
        int a, b, u, v, id;
        bool operator < (const edge &that) const {return D ? a < that.a : b < that.b;}
    }e[N], q[N];
    struct node{int x[2], y[2]; edge *e;}t[N*5];
    struct stack{int x, d, a, b;}sta[N];
    int find(int x){return f[x]==x?x:find(f[x]);}
    void merge(int a, int b, int va, int vb)
    {
        a = find(a), b = find(b);
        if(a == b)sta[++stacnt] = (stack){a, a, fva[a], fvb[a]},cmax(fva[a], va),cmax(fvb[a], vb);
        else if(dep[a]<dep[b])f[a] = b, sta[++stacnt] = (stack){a, dep[b], fva[b], fvb[b]}, cmax(fva[b], max(va,fva[a])), cmax(fvb[b], max(vb,fvb[a]));
        else if(dep[a]>dep[b])f[b] = a, sta[++stacnt] = (stack){b, dep[a], fva[a], fvb[a]}, cmax(fva[a], max(va,fva[b])), cmax(fvb[a], max(vb,fvb[b]));
        else ++dep[a], f[b] = a, sta[++stacnt] = (stack){b, dep[a] - 1, fva[a], fvb[a]}, cmax(fva[a], max(va,fva[b])), cmax(fvb[a], max(vb,fvb[b]));
    }
    void recover(){int x = sta[stacnt].x; fva[f[x]] = sta[stacnt].a, fvb[f[x]] = sta[stacnt].b; dep[f[x]] = sta[stacnt].d; f[x] = x; --stacnt;}
    void pushup(int x)
    {
        t[x].x[0] = min(t[x<<1].x[0], t[x<<1|1].x[0]);t[x].x[1] = max(t[x<<1].x[1], t[x<<1|1].x[1]);
        t[x].y[0] = min(t[x<<1].y[0], t[x<<1|1].y[0]);t[x].y[1] = max(t[x<<1].y[1], t[x<<1|1].y[1]);
    }
    void build(int x, int l, int r, int d)
    {
        D = d; int mid = (l+r)>>1; nth_element(q+l, q+mid, q+r+1); t[x].e = NULL;
        if(l == r){t[x].x[0] = t[x].x[1] = q[mid].a, t[x].y[0] = t[x].y[1] = q[mid].b; t[x].e = &q[mid]; return;}
        build(x<<1,l,mid,d^1); build(x<<1|1,mid+1,r,d^1); pushup(x);
    }
    void dc(int x, int cnt)
    {
        int curcnt = stacnt;
        for(int i = 1; i <= cnt; i++) if(e[i].a <= t[x].x[0] && e[i].b <= t[x].y[0]) merge(e[i].u,e[i].v,e[i].a,e[i].b);
        if(t[x].e)
        {
            edge *E = t[x].e; int tmp = find(E->u);
            if(find(E->v) == tmp && fva[tmp] == E->a && fvb[tmp] == E->b) ans[E->id] = 1;
        }
        else
        {
            int tmp = 0; 
            for(int i = 1; i <= cnt; i++) 
                if(!(e[i].a <= t[x].x[0] && e[i].b <= t[x].y[0]) && e[i].a <= t[x<<1].x[1] && e[i].b <= t[x<<1].y[1]) 
                    ++tmp, swap(e[i], e[tmp]); dc(x<<1,tmp);
            tmp = 0; 
            for(int i = 1; i <= cnt; i++) 
                if(!(e[i].a <= t[x].x[0] && e[i].b <= t[x].y[0]) && e[i].a <= t[x<<1|1].x[1] && e[i].b <= t[x<<1|1].y[1]) 
                    ++tmp, swap(e[i], e[tmp]); dc(x<<1|1,tmp);
        }
        for(; stacnt != curcnt; recover());
    }
    void main()
    {
        scanf("%d%d",&n,&m); for(int i = 1; i <= m; i++) scanf("%d%d%d%d",&e[i].u,&e[i].v,&e[i].a,&e[i].b);
        scanf("%d",&qcnt); for(int i = 1; i <= qcnt; i++) scanf("%d%d%d%d",&q[i].u,&q[i].v,&q[i].a,&q[i].b), q[i].id = i;
        for(int i = 1; i <= n; i++) f[i] = i, fva[i] = fvb[i] = -1;
        build(1,1,qcnt,0); dc(1,m);
        for(int i = 1; i <= qcnt; i++) puts(ans[i] ? "Yes" : "No");
    }
}
int main()
{
    runzhe2000::main();
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值