[HNOI2016]最小公倍数

news/2024/6/29 11:55:11

题目描述

给定一张N个顶点M条边的无向图(顶点编号为1,2,…,n),每条边上带有权值。所有权值都可以分解成2^a*3^b
的形式。现在有q个询问,每次询问给定四个参数u、v、a和b,请你求出是否存在一条顶点u到v之间的路径,使得
路径依次经过的边上的权值的最小公倍数为2^a*3^b。注意:路径可以不是简单路径。下面是一些可能有用的定义
:最小公倍数:K个数a1,a2,…,ak的最小公倍数是能被每个ai整除的最小正整数。路径:路径P:P1,P2,…,Pk是顶
点序列,满足对于任意1<=i<k,节点Pi和Pi+1之间都有边相连。简单路径:如果路径P:P1,P2,…,Pk中,对于任意1
<=s≠t<=k都有Ps≠Pt,那么称路径为简单路径。

题解

大意就是说,给出一张无向图,每条边有两种边权ab,每次询问两个点和AB,问是否能找出一条路径,使得路径上所有的a都<=A,所有的b都<=B,而且路径上最大的a=A,最大的b=B。

首先考虑只有一种边权怎么做,其实就是类似克鲁斯卡尔的过程,从小到大加边,每个联通块内再维护一个最大边权,边做边处理询问就好了。

现在多了一维,怎么办。

诶,我们想到了处理多维问题的利器——KD-tree。

做法就是对于询问建KD-tree,然后把每条边在KD-tree上走一遍,挂在符合要求的节点上,然后dfs一遍这颗KD-tree,用一个带撤销的并查集维护就好了。

这是一种解法,然鹅标算是分块。

我们把边按照a排序,询问按照b排序,然后再把所有的边分块。

对于每一块,我们都把所有询问的a在这个块内的询问拿进来,这个暴力扫一遍就好了。

然后把这个块之前的所有边按照b排序。

然后处理每个询问,因为每个询问的b都是单调不降的,在当前块前面的边的b因为排过序,也是单挑不降的,而且那些边的a<=A,所以用双指针维护加边操作。

对于当前块的边,每次暴力扫暴力加,查询完之后暴力撤销回去。

细节&&卡常

于是我T飞了。

发现我的一个询问会出现在多个块里,这样就会很慢,我们希望一个询问如果能出现在多个块里的话,会出现早最后一个块中。

所以要这么写

if(q[j].a>=b[l].a&&(r==m||q[j].a<b[r+1].a))

代码

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath> 
#define N 50002
#define M 100009
using namespace std;
int n,m,f[N],n1,be[M],bi,Q,deep[N],mxa[N],mxb[N],st[M];
bool ans[M];
inline int rd(){
    int x=0;char c=getchar();bool f=0;
    while(!isdigit(c)){if(c=='-')f=1;c=getchar();}
    while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=getchar();}
    return f?-x:x;
}
struct rubbish{int x,y,dep,mxa,mxb;bool tag;}ru[M];
struct node{int u,v,a,b,id;}q[M],b[M];
inline bool cmpa(node a,node b){
  if(a.a!=b.a)return a.a<b.a;else return a.b<b.b;
}
inline bool cmpb(node a,node b){
  if(a.b!=b.b)return a.b<b.b;else return a.a<b.a;
} 
inline int find(int x){return f[x]==x?x:find(f[x]);}
inline void merge(int x,int y,int a,int b){;
    int xx=find(x),yy=find(y);
    if(deep[xx]>deep[yy])swap(xx,yy);
    ++bi;
    ru[bi].x=xx;ru[bi].y=yy;ru[bi].dep=deep[yy];
    ru[bi].mxa=mxa[yy];ru[bi].mxb=mxb[yy];ru[bi].tag=0;
    if(xx!=yy){
        f[xx]=yy;
        deep[yy]=max(deep[yy],deep[xx]+1);
        ru[bi].tag=1;
        mxa[yy]=max(mxa[xx],mxa[yy]);mxb[yy]=max(mxb[xx],mxb[yy]);
    }
    mxa[yy]=max(mxa[yy],a);mxb[yy]=max(mxb[yy],b);
}
inline void pop(int x){
    if(x==-1){bi=0;return;}
    while(x--){
        int x=ru[bi].x,y=ru[bi].y,dep=ru[bi].dep;
        deep[y]=dep;if(ru[bi].tag)f[x]=x;
        mxa[y]=ru[bi].mxa;mxb[y]=ru[bi].mxb;
        bi--;
    }
}
int main(){
    n=rd();m=rd();
    n1=sqrt((double)m*log2(n)); 
    for(int i=1;i<=m;++i){
        b[i].u=rd();b[i].v=rd();b[i].a=rd();b[i].b=rd();
    }
    Q=rd();
    for(int i=1;i<=Q;++i)q[i].u=rd(),q[i].v=rd(),q[i].a=rd(),q[i].b=rd(),q[i].id=i;
    sort(b+1,b+m+1,cmpa);sort(q+1,q+Q+1,cmpb);
    int bb=(m-1)/n1+1;
    for(int i=1;i<=bb;++i){   //扫描所有边块 
        int l=(i-1)*n1+1,r=min(m,i*n1);int top=0;
        for(int j=1;j<=Q;++j)if(q[j].a>=b[l].a&&(r==m||q[j].a<b[r+1].a))st[++top]=j;
        sort(b+1,b+l,cmpb);
        for(int j=1;j<=n;++j){f[j]=j;deep[j]=1;mxa[j]=mxb[j]=-1;}
        for(int j=1,k=1;j<=top;++j){
            int num=0;
            for(;k<l&&b[k].b<=q[st[j]].b;++k)merge(b[k].u,b[k].v,b[k].a,b[k].b);
            for(int o=l;o<=r;++o)if(b[o].a<=q[st[j]].a&&b[o].b<=q[st[j]].b)
              merge(b[o].u,b[o].v,b[o].a,b[o].b),num++;
            int xx=find(q[st[j]].u),yy=find(q[st[j]].v);
            if(xx==yy&&mxa[xx]==q[st[j]].a&&mxb[xx]==q[st[j]].b)ans[q[st[j]].id]=1;
            pop(num);
        }
        pop(-1);
    }
    for(int i=1;i<=Q;++i)if(ans[i])puts("Yes");
    else puts("No");
    return 0;
}

转载于:https://www.cnblogs.com/ZH-comld/p/10270151.html


http://www.niftyadmin.cn/n/4260297.html

相关文章

再续 asp.net 域名欺骗式开发之泛解析域名

前言&#xff1a; 在很久前&#xff0c;曾发布过一篇&#xff1a;asp.net 域名欺骗式开发有不少新新人类表示对此文不屑&#xff0c;觉得太基础&#xff0c;他们早懂了&#xff0c;懂了就懂了&#xff0c;毕竟还有人还没有懂的。今天再续文&#xff0c;讲解域名欺骗式开发的进阶…

Dave Python 练习十五 -- 面向对象编程

#encodingutf-8 ### *************** 面向对象编程 ******************** #*********** Part 1: 面向对象编程 *********************** #面向对象编程踩上了进化的步伐&#xff0c;增强了结构化编程&#xff0c;实现了数据与动作的融合&#xff1a;数据层和逻 #辑层现在由一个…

机器学习入门-数据过采样(上采样)1. SMOTE

from imblearn.over_sampling import SMOTE # 导入 overstamp SMOTE(random_state0) # 对训练集的数据进行上采样&#xff0c;测试集的数据不需要SMOTE_train_x, SMOTE_train_y overstamp.fit_sample(train_x, train_y) 由于数据分布的不均衡&#xff0c;因此对数据进行上采…

全民娱乐 手机电视将成为3G手机最主要应用

全民娱乐手机电视将成为3G手机最主要应用<?xml:namespace prefix o ns "urn:schemas-microsoft-com:office:office" />究竟什么才是3G时代的主要应用&#xff1f;对于这个问题一直也是仁者见仁智者见智&#xff0c;大家心中都有自己的一杆秤。自己的喜好自然…

数据结构的定义和简介

1. 概述数据结构定义:我们如何把现实中大量而复杂的问题以特定的数据类型和特定的存储结构保存到主存储器(内存)中,以及在此基础上为实现某个功能(如元素的CURD、排序等)而执行的相应操作&#xff0c;这个相应的操作也叫算法。数据结构 元素 元素的关系算法 对数据结构的操作…

查看交换机中的DHCP配置情况

查看交换机中的DHCP配置情况 查看DHCP配置情况 display dhcp server statistics 查看地址池已分配的地址 display dhcp server ip-in-use 查看地址池剩余没有分配的地址 display dhcp server free-ip 查看地址池中已经过期的地址 display dhcp server expried

使用docker-composer创建一个mysql容器,并创建一个database且指定其编码集为中文utf8...

2019独角兽企业重金招聘Python工程师标准>>> #适用于docker-compser的v2 version: 2 services:mysql:container_name: mysqlimage: mariadb:10.4.1environment:#最好使用此设定时区&#xff0c;其它静像也可以使用- TZCST-8- CORE_VM_DOCKER_HOSTCONFIG_NETWORKMODE…

H3C交换机端口镜像配置

H3C交换机端口镜像配置 1、进入配置模式&#xff1a;system-view&#xff1b; 2、创建本地镜像组&#xff1a;mirroring-group 1 local 3、为镜像组配置源端口&#xff1a;mirroring-group 1 mirroring-port 4、为镜像组配置目的端口&#xff1a;mirroring-group 1 monitor-po…