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之间的路径,使得

路径依次经过的边上的权值的最小公倍数为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

分块好题

问题转化为:每个边有两个权值a和b,每次询问两点之间是否存在一条路径满足路径上a的最大值和b的最大值分别等于一个数。

把边按照a排序,然后分块。

每次处理a的范围在当前块内的询问。假设处理到第i块,把询问和前i-1块按照b排序,这些部分O(n)就可以处理了。第i块里的边暴力处理,然后撤销修改。然后用一个并查集维护集合最大值。

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<algorithm>
#define F(i,j,n) for(int i=j;i<=n;i++)
#define D(i,j,n) for(int i=j;i>=n;i--)
#define ll long long
#define N 200005
#define inf 1000000000
using namespace std;
int n,m,q,block,cnt,tot,f[N],sz[N],mxa[N],mxb[N];
bool ans[N];
struct data{int x,y,a,b,id;}a[N],b[N],c[N];
struct opt{int x,y,f,mxa,mxb,sz;}op[N];
inline int read()
{
	int x=0,f=1;char ch=getchar();
	while (ch<'0'||ch>'9'){if (ch=='-') f=-1;ch=getchar();}
	while (ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
	return x*f;
}
inline bool cmpa(const data &a,const data &b){return a.a==b.a?a.b<b.b:a.a<b.a;}
inline bool cmpb(const data &a,const data &b){return a.b==b.b?a.a<b.a:a.b<b.b;}
int find(int x,int flg)
{
	if (f[x]==x) return x;
	if (flg) op[++tot]=(opt){x,0,f[x],0,0,0};
	return f[x]=find(f[x],flg);
}
void merge(int x,int y,int a,int b,int flg)
{
	x=find(x,flg);y=find(y,flg);
	if (sz[x]<sz[y]) swap(x,y);
	if (flg) op[++tot]=(opt){x,y,f[y],mxa[x],mxb[x],sz[x]};
	if (x==y){mxa[x]=max(mxa[x],a);mxb[x]=max(mxb[x],b);return;}
	f[y]=x;sz[x]+=sz[y];
	mxa[x]=max(mxa[x],max(mxa[y],a));
	mxb[x]=max(mxb[x],max(mxb[y],b));
}
int main()
{
	n=read();m=read();block=sqrt(m);
	F(i,1,m){a[i].x=read();a[i].y=read();a[i].a=read();a[i].b=read();a[i].id=i;}
	sort(a+1,a+m+1,cmpa);
	q=read();
	F(i,1,q){b[i].x=read();b[i].y=read();b[i].a=read();b[i].b=read();b[i].id=i;}
	sort(b+1,b+q+1,cmpb);
	for(int i=1;i<=m;i+=block)
	{
		cnt=0;
		F(j,1,q) if (b[j].a>=a[i].a&&(i+block>m||b[j].a<a[i+block].a)) c[++cnt]=b[j];
		sort(a+1,a+i,cmpb);
		F(j,1,n) f[j]=j,sz[j]=1,mxa[j]=mxb[j]=-inf;
		for(int j=1,k=1;j<=cnt;j++)
		{
			for(;k<i&&a[k].b<=c[j].b;k++) merge(a[k].x,a[k].y,a[k].a,a[k].b,0);
			tot=0;
			F(l,i,min(i+block-1,m)) if (a[l].a<=c[j].a&&a[l].b<=c[j].b) merge(a[l].x,a[l].y,a[l].a,a[l].b,1);
			int x=find(c[j].x,1),y=find(c[j].y,1);
			ans[c[j].id]=(x==y)&&(mxa[x]==c[j].a)&&(mxb[x]==c[j].b);
			D(l,tot,1)
			{
				if (!op[l].y) f[op[l].x]=op[l].f;
				else{f[op[l].y]=op[l].f;mxa[op[l].x]=op[l].mxa;mxb[op[l].x]=op[l].mxb;sz[op[l].x]=op[l].sz;}
			}
		}
	}
	F(i,1,q) puts(ans[i]?"Yes":"No");
	return 0;
}
时间: 2024-11-05 02:22:29

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之间的路径,使得 路径依次经过的边上的权值的最

BZOJ4537 : [Hnoi2016]最小公倍数

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

【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:P

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

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

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

YCB 的暑期计划

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

HDU 1108: 最小公倍数

最小公倍数 #include<iostream> using namespace std; int gcd(int a, int b) { while (b) { int t = a%b; a = b; b = t; } return a; } int lcm(int a, int b) { return a / gcd(a, b)*b; } int main() { ios::sync_with_stdio(false); int a, b; while (cin >> a &g