[BZOJ4537] [HNOI/AHOI2016] 最小公倍数 - 分块 - 并查集

考场爆零很是不爽……

好吧讲道理这题其实很裸

4537: [Hnoi2016]最小公倍数

Time Limit: 40 Sec   Memory Limit: 512 MB
Submit: 575   Solved: 243
[ Submit][ Status][ Discuss]

Description

  给定一张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<=ii和Pi+1之间都有边相连。简单路径:如果路径P:P1,P2,…,Pk中,对于任意1
<=s≠t<=k都有Ps≠Pt,那么称路径为简单路径。

Input

  输入文件的第一行包含两个整数N和M,分别代表图的顶点数和边数。接下来M行,每行包含四个整数u、v、a、
b代表一条顶点u和v之间、权值为2^a*3^b的边。接下来一行包含一个整数q,代表询问数。接下来q行,每行包含四
个整数u、v、a和b,代表一次询问。询问内容请参见问题描述。1<=n,q<=50000、1<=m<=100000、0<=a,b<=10^9

Output

  对于每次询问,如果存在满足条件的路径,则输出一行Yes,否则输出一行 No(注意:第一个字母大写,其余
字母小写) 。

Sample Input

4 5
1 2 1 3
1 3 1 2
1 4 2 1
2 4 3 2
3 4 2 2
5
1 4 3 3
4 2 2 3
1 3 2 2
2 3 2 2
1 3 4 4

Sample Output

Yes
Yes
Yes
No
No

HINT

Source

[ Submit][ Status][ Discuss]

HOME   Back

首先我们会想到一个暴力做法,那就是对于每个询问,插入所有满足a不大于询问的a,且b不大于询问的b的边并判断是否u和v是否在一个a的最大值即询问的a,b的最大值即询问的b的连通块内。显然这样做是O(mq)的,显然要TLE。

那么想到这就不难想到分块了。首先我们对边直接分块,先关于权值a排序,每块的大小是m^0.5,共有m^0.5个块。

然后我们可以将询问分配到每个块中。对于每个块中的所有询问,我们考虑对这个块以前的所有边暴力按b排序,因为这些边权显然都满足a比询问的a小,对于一个块中的询问可以在O(m log m)的时间内解决这部分的边,可以将它们插入并查集。

然后还剩下一些零零散散的边,即在询问所在的权值块内,由于边只有m^0.5条,即使我们对于所有的询问暴力枚举查找可以加的边也是O(m^0.5)每个询问的,这时我们就要考虑如何使并查集在加了边之后又能回到之前的状态。

其实很简单,我们知道并查集如果按秩合并的话最坏的高度也就只有logn层(完全二叉树),因此我们可以考虑不进行路径压缩,只是按秩合并,将修改前的记录保存入一个栈内,每次询问结束时复原即可。

时间复杂度 O((m log m+q log n)m^0.5)

/**************************************************************
    Problem: 4537
    User: whzzt
    Language: C++
    Result: Accepted
    Time:11088 ms
    Memory:19648 kb
****************************************************************/
 
#include "stdio.h"
#include "algorithm"
#include "iostream"
#include "string.h"
#include "stdlib.h"
#include "math.h"
#include "vector"
#include "map"
#include "set"
 
using namespace std;
 
const int L=10000005;
char _buff[L]; int _pos=-1;
void ReadIn(){fread(_buff,L,sizeof(char),stdin);}
#define fge _buff[++_pos]
inline int read(){
    int x=0; char ch=fge;
    while(ch>'9'||ch<'0')ch=fge;
    while(ch<='9'&&ch>='0')
        x=x*10+ch-'0',ch=fge;
    return x;
}
const int N=50005,M=100005;
struct E{int u,v,a,b;} e[M],q[M],st[M],es[M];
inline bool cmpa(const E&a,const E&b)
{ return a.a==b.a?a.bsiz[fv])swap(fu,fv);
    if(type){ 
        rq[++tmp]=fv;sz[tmp]=siz[fv];p[tmp]=fu;
        ma[tmp]=maxa[fv];mb[tmp]=maxb[fv];
    }
    if(fu!=fv){
        fa[fu]=fv;siz[fv]+=siz[fu];
        maxa[fv]=max(maxa[fu],maxa[fv]);
        maxb[fv]=max(maxb[fu],maxb[fv]);
    }
    maxa[fv]=max(maxa[fv],a.a);
    maxb[fv]=max(maxb[fv],a.b);
}
 
int main(){
    ReadIn();n=read(),m=read();int i,j,k;
    for(i=1;i<=m;i++)init(e[i]);Q=read();
    for(i=1;i<=Q;i++)init(q[i]);lim=(int)sqrt(m+.1);
    sort(e+1,e+m+1,cmpa);
    for(i=0;i<=m;i+=lim){
        l=i+1,r=min(i+lim,m);
        for(j=1,top=0;j<=Q;j++)
            if(q[j].a>=e[l].a&&(q[j].a


你可能感兴趣的:(数据结构)