【BZOJ4537】[Hnoi2016]最小公倍数 分块

【BZOJ4537】[Hnoi2016]最小公倍数

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<=i<k,节点Pi和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

题解:最小公倍数=2^a*3^b等价于存在一个连通块使得边权的2的次数最大值=a且3的次数最大值=b,先给出一种暴力方法:

将所有边和询问按a从小到大排序,然后对于每个询问,将a不超过它的所有边按b排序,然后从小到大扔到并查集中,并用并查集维护连通块中b的最大值,如果最后b的最大值=询问的最大值,则输出Yes。

如何优化这个过程呢?考虑按a分块,先将所有询问按(a值所在块-1)排序,然后我们处理第i块中的询问。先将前i块的所有边以及询问按b排序,然后一个一个处理询问和连边。但问题是,对当前询问产生贡献的某些边可能在第i+1块中,但是这些边不超过sqrt(n)个,于是用按秩合并的并查集暴力加入再暴力还原即可。(我写的是按siz合并)。

#include <iostream>
#include <cstring>
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cmath>
using namespace std;
const int maxn=50010;
int n,m,Q,B,ltp,tp,tq,ltq,top;
struct edge
{
	int a,b,A,B,org;
}p[maxn<<1],q[maxn];
int ans[maxn],f[maxn],sz[maxn],sa[maxn],sb[maxn],px[maxn],psa[maxn],psb[maxn],psz[maxn],pf[maxn],vis[maxn];
bool cmpa(const edge &a,const edge &b)
{
	return (a.A==b.A)?(a.B<b.B):(a.A<b.A);
}
bool cmpb(const edge &a,const edge &b)
{
	return (a.B==b.B)?(a.A<b.A):(a.B<b.B);
}
inline int rd()
{
	int ret=0,f=1;	char gc=getchar();
	while(gc<‘0‘||gc>‘9‘)	{if(gc==‘-‘)f=-f;	gc=getchar();}
	while(gc>=‘0‘&&gc<=‘9‘)	ret=ret*10+gc-‘0‘,gc=getchar();
	return ret*f;
}
int find(int x)
{
	return (f[x]==x)?x:find(f[x]);
}
void link(int x)
{
	int a=find(p[x].a),b=find(p[x].b);
	if(a==b)
	{
		sa[a]=max(sa[a],p[x].A),sb[a]=max(sb[a],p[x].B);
		return ;
	}
	if(sz[a]<sz[b])	swap(a,b);
	f[b]=f[a],sz[a]+=sz[b],sa[a]=max(sa[a],max(sa[b],p[x].A)),sb[a]=max(sb[a],max(sb[b],p[x].B));
}
void add(int x)
{
	if(!vis[x])	vis[x]=1,px[++top]=x;
}
int main()
{
	n=rd(),m=rd(),B=int(sqrt(double(m)));
	int i,j,k,l,ra,rb;
	for(i=1;i<=m;i++)	p[i].a=rd(),p[i].b=rd(),p[i].A=rd(),p[i].B=rd();
	sort(p+1,p+m+1,cmpa);
	Q=rd();
	for(i=1;i<=Q;i++)	q[i].a=rd(),q[i].b=rd(),q[i].A=rd(),q[i].B=rd(),q[i].org=i;
	sort(q+1,q+Q+1,cmpa);
	p[0].A=p[0].B=-1,p[m+1].A=p[m+1].B=1<<30;
	for(i=0;i<=m;ltq=tq,i=tp+B)
	{
		tp=min(i,m);
		for(;tq<Q&&q[tq+1].A>=p[tp].A&&q[tq+1].A<p[min(tp+B,m+1)].A;tq++);
		sort(p+1,p+tp+1,cmpb),sort(q+ltq+1,q+tq+1,cmpb);
		for(j=1;j<=n;j++)	f[j]=j,sa[j]=sb[j]=-1,sz[j]=1;
		for(l=1,j=ltq+1;j<=tq;j++)
		{
			for(;l<=tp&&p[l].B<=q[j].B;link(l++));
			top=0;
			for(k=tp+1;p[k].A<=q[j].A;k++)	if(p[k].B<=q[j].B)
				ra=find(p[k].a),rb=find(p[k].b),add(ra),add(rb);
			for(k=1;k<=top;k++)	psa[k]=sa[px[k]],psb[k]=sb[px[k]],psz[k]=sz[px[k]];
			for(k=tp+1;p[k].A<=q[j].A;k++)	if(p[k].B<=q[j].B)	link(k);
			ra=find(q[j].a),rb=find(q[j].b);
			if(ra==rb&&sa[ra]==q[j].A&&sb[ra]==q[j].B)	ans[q[j].org]=1;
			for(k=1;k<=top;k++)	f[px[k]]=px[k],sa[px[k]]=psa[k],sb[px[k]]=psb[k],sz[px[k]]=psz[k],vis[px[k]]=0;
		}
	}
	for(i=1;i<=Q;i++)
	{
		if(ans[i])	printf("Yes\n");
		else	printf("No\n");
	}
	return 0;
}//
时间: 2024-10-13 22:44:51

【BZOJ4537】[Hnoi2016]最小公倍数 分块的相关文章

[BZOJ4537][HNOI2016]最小公倍数(分块+并查集)

4537: [Hnoi2016]最小公倍数 Time Limit: 40 Sec  Memory Limit: 512 MBSubmit: 1687  Solved: 607[Submit][Status][Discuss] Description 给定一张N个顶点M条边的无向图(顶点编号为1,2,…,n),每条边上带有权值.所有权值都可以分解成2^a*3^b 的形式.现在有q个询问,每次询问给定四个参数u.v.a和b,请你求出是否存在一条顶点u到v之间的路径,使得 路径依次经过的边上的权值的最

4537: [Hnoi2016]最小公倍数|分块

暴力的做法就是直接找到所有a,b都小于等于某个询问的边然后并查集合并,维护每个集合的a,b得最大值看是否等于询问的a,b 然后就可以考虑分块,把边按照a排序,每隔n?√分为一块 块前的按照b值排序按顺序插入,块内的暴力判断,并查集合并,每次都把块内合并的记录下来,处理完某个询问时就撤回并查集的操作 块的大小为n?√可能会T 改成n?log2n????????√可能会快一点 #include<algorithm> #include<iostream> #include<cstd

BZOJ4537 : [Hnoi2016]最小公倍数

将边按$a$从小到大排序,每$\sqrt{m}$个取一个关键点. 对于每个关键点,将这个点之前的边以及要在这个关键点回答的询问按$b$排序. 依次加入这个关键点之前的每条边,用并查集维护每个连通块$a$和$b$的最大值. 对于零碎部分,只有$\sqrt{m}$条边,暴力加入即可. 用一个栈按时间记录所有修改操作,然后撤销操作即可. 时间复杂度$O(m\sqrt{m}\log n)$. #include<cstdio> #include<algorithm> using namesp

BZOJ 4537: [Hnoi2016]最小公倍数

4537: [Hnoi2016]最小公倍数 Time Limit: 40 Sec  Memory Limit: 512 MBSubmit: 1084  Solved: 400[Submit][Status][Discuss] Description 给定一张N个顶点M条边的无向图(顶点编号为1,2,…,n),每条边上带有权值.所有权值都可以分解成2^a*3^b的形式.现在有q个询问,每次询问给定四个参数u.v.a和b,请你求出是否存在一条顶点u到v之间的路径,使得路径依次经过的边上的权值的最小公

4537: [Hnoi2016]最小公倍数

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<=i<

2017.7.22 hnoi2016 最小公倍数

#include<cmath> #include<cstdio> #include<iostream> #include<algorithm> #define N 50010 #define M 100010 #define RG register #define inf 0x3f3f3f3f using namespace std; bool ans[N]; struct stac{ int u,v,op; }sta[40000]; struct node

bzoj4537【HNOI2016】最小公倍数

4537: [Hnoi2016]最小公倍数 Time Limit: 40 Sec  Memory Limit: 512 MB Submit: 563  Solved: 236 [Submit][Status][Discuss] Description 给定一张N个顶点M条边的无向图(顶点编号为1,2,-,n),每条边上带有权值.所有权值都可以分解成2^a*3^b 的形式.现在有q个询问,每次询问给定四个参数u.v.a和b,请你求出是否存在一条顶点u到v之间的路径,使得 路径依次经过的边上的权值的

YCB 的暑期计划

前言 YCB现在很弱(TAT) 暑假有一个月,赶快狂补一下. 大概的计划如下: 首先前期会以数据结构为主,毕竟代码能力太弱,涉及内容:线段树分治.二进制分组.KD-Tree. 等数据结构做到没有智商的时候加入一波数论,内容为 杜教筛.min_25筛. 然后中途小清新一下,做一些 组合博弈与构造题. 接着继续练代码能力,顺便学一些神奇的暴力:启发式合并.dsu on tree . 然后图论也忘的差不多了,就回过头去学点新东西,大概会有spfa判负环.0/1分数规划.差分约束. 估计这个时候也没有什

bzoj4539【HNOI2016】树

4539: [Hnoi2016]树 Time Limit: 40 Sec  Memory Limit: 256 MB Submit: 415  Solved: 157 [Submit][Status][Discuss] Description 小A想做一棵很大的树,但是他手上的材料有限,只好用点小技巧了.开始,小A只有一棵结点数为N的树,结 点的编号为1,2,-,N,其中结点1为根:我们称这颗树为模板树.小A决定通过这棵模板树来构建一颗大树.构建过 程如下:(1)将模板树复制为初始的大树.(2)