首页 > 其他分享 >P3247 [HNOI2016] 最小公倍数 解题报告

P3247 [HNOI2016] 最小公倍数 解题报告

时间:2025-01-15 17:43:33浏览次数:1  
标签:le 50005 公倍数 mina maxa qa HNOI2016 include P3247

前置知识:可撤销并查集

用一个栈记录合并顺序,每次撤销将栈顶的元素恢复

但是这种方法不能路径压缩,因为会改变节点之间的关系,为了保证时间,可以按照 \(size\) 进行合并


题意显然为能不能找到一条路径,使这条路径上最大的 \(a\) 为 \(qa\),最大的 \(b\) 为 \(qb\)

因为有a和b两个限制,考虑按照a和b的大小分块

因为数的范围很大,所以可以按照先将边按a排序后的位置对应的值分块

对于每一段,记最小值为 \(mina\),最大值为\(maxa\)

先将满足 \(mina\le qa\le maxa\) 的询问加入s中

接下来考虑b的限制,因为此时a的问题已经解决了,所以可以对 \(a\le mina\)的边按b从小到大排序

为了便于边的插入和删除,可以先将询问按b从小到大排序

此时,依次枚举s中的询问,加入满足 \(a\le mina,b\le qb\) 的边,因为b是递增的,所以这些边加入后始终满足条件,不用删除

对于 \(mina\le qa\le maxa\) 的边,因为不一定满足条件,所以要利用可撤销并查集,对于每个询问,只加入符合条件的边,用完后撤销

此时,如果所求的u和v在同一个连通块,且块内最大的a和b均与所求相等,则答案为Yes,否则为No

代码:

#include<cstdio>
#include<algorithm>
#include<cmath>
using namespace std;
int n,m,q,blen,cnt,ans[50005];
struct node{
	int u,v,a,b,id;
}e[100005],qs[50005],s[50005];
bool cmp1(node x,node y)
{
	if(x.a==y.a) return x.b<y.b;
	return x.a<y.a;
}
bool cmp2(node x,node y)
{
	if(x.b==y.b) return x.a<y.a;
	return x.b<y.b;
}
int f[500005],siz[100005],maxa[100005],maxb[100005],top;
int find(int x)
{
	if(f[x]!=x) return find(f[x]);
	return x;
}
struct node1{
	int x,y,maxa,maxb,siz,fa;
}st[100005];
void merge(int u,int v,int a,int b)
{
	int x=find(u),y=find(v);
	if(siz[x]<siz[y])
	{
		swap(x,y),swap(u,v);
	}
	top++;
	st[top]=(node1){x,y,maxa[x],maxb[x],siz[x],f[y]};
	if(x==y)
	{
		maxa[x]=max(maxa[x],a);
		maxb[x]=max(maxb[x],b);
		return;
	}
	f[y]=x;
	siz[x]+=siz[y];
	maxa[x]=max(maxa[x],max(maxa[y],a));
	maxb[x]=max(maxb[x],max(maxb[y],b));
}
void split()
{
	while(top)
	{
		siz[st[top].x]=st[top].siz;
		maxa[st[top].x]=st[top].maxa;
		maxb[st[top].x]=st[top].maxb;
		f[st[top].y]=st[top].fa;
		top--;
	}
}
int main()
{
	scanf("%d%d",&n,&m);
	for(int i=1;i<=m;i++)
	{
		int u,v,a,b;
		scanf("%d%d%d%d",&u,&v,&a,&b);
		e[i]=(node){u,v,a,b,i};
	}
	scanf("%d",&q);
	for(int i=1;i<=q;i++)
	{
		int u,v,a,b;
		scanf("%d%d%d%d",&u,&v,&a,&b);
		qs[i]=(node){u,v,a,b,i};
	}
	sort(e+1,e+m+1,cmp1);
	sort(qs+1,qs+q+1,cmp2);
	blen=sqrt(m);
	for(int i=1;i<=m;i+=blen)
	{
		cnt=0;
		for(int j=1;j<=q;j++)
		{
			if(qs[j].a>=e[i].a&&(i+blen>m||qs[j].a<e[i+blen].a))
			{
				s[++cnt]=qs[j];
			}
		}
		sort(e+1,e+i+1,cmp2);
		for(int j=1;j<=n;j++)
		{
			f[j]=j,siz[j]=1;
			maxa[j]=maxb[j]=-1;
		}
		for(int j=1,k=1;j<=cnt;j++)
		{
			while(k<i&&e[k].b<=s[j].b)
			{
				merge(e[k].u,e[k].v,e[k].a,e[k].b);
				k++;
			}
			top=0;
			for(int l=i;l<i+blen&&l<=m;l++)
			{
				if(e[l].a<=s[j].a&&e[l].b<=s[j].b)
				{
					merge(e[l].u,e[l].v,e[l].a,e[l].b);
				}
			}
			int x=find(s[j].u),y=find(s[j].v);
			if(x==y&&maxa[x]==s[j].a&&maxb[x]==s[j].b) ans[s[j].id]=1;
			split();
		}
	}
	for(int i=1;i<=q;i++)
	{
		if(ans[i]) printf("Yes\n");
		else printf("No\n");
	}
	return 0;
}

标签:le,50005,公倍数,mina,maxa,qa,HNOI2016,include,P3247
From: https://www.cnblogs.com/wangsiqi2010916/p/18673500

相关文章

  • P3247 [HNOI2016] 最小公倍数 题解
    \(\text{P3247[HNOI2016]最小公倍数题解}\)第一眼上去没什么明显的思路。图上问题一般没有什么好的多项式复杂度算法来解决。观察数据范围,注意到\(n,q\le5\times10^4\),是一个典型的根号复杂度算法,于是考虑分块来处理。注意到所求的不一定是简单路径,也就是在不超过所需要的......
  • HNOI2016 序列 题解
    HNOI2016序列题解我做离线版本时往了偏序方向想,但是发现非常麻烦。直到看到了在线版本的容斥做法,发现既好写又跑得快。首先考虑容斥,我们不妨把一个询问\([L,R]\)中最小值的位置\(pos\)求出来。子区间跨过\(pos\),贡献即\((pos-L+1)\times(R-pos+1)\timesa_{pos}\)。......
  • 写一个方法找出两个数的最小公倍数
    在前端开发中,你可以使用JavaScript来写一个方法找出两个数的最小公倍数(LeastCommonMultiple,LCM)。最小公倍数可以通过两数的乘积除以它们的最大公约数(GreatestCommonDivisor,GCD)来得到。以下是一个简单的JavaScript函数,用于计算两个数的最小公倍数:functiongcd(a,b){......
  • C语言求最小公倍数
    intmain(){ inta=0,b=0; scanf("%d%d",&a,&b); intmin=(a>b)?a:b; while(1){ if(min%a==0&&min%b==0)break; min++; } printf("最小公倍数为:%d",min); return0;}1.因为最小公倍数能够同时被这两个数整除2.......
  • 「Mac玩转仓颉内测版48」小学奥数篇11 - 最大公约数与最小公倍数
    本篇将通过Python和Cangjie双语实现最大公约数(GCD)和最小公倍数(LCM)的计算。这个题目帮助学生理解如何运用数学算法,并将其与编程实现结合。关键词小学奥数Python+Cangjie最大公约数(GCD)最小公倍数(LCM)一、题目描述编写一个程序,接收两个正整数,计算并输出它们的最大公......
  • C++入门基础知识91(实例)——实例16【求两数最小公倍数】
    成长路上不孤单......
  • 最大公约数与最小公倍数
    前言:  最大公约数(最大公因数)是指两个或多个整数共有约数中最大的一个。最小公倍数是指两个或多个整数的公倍数里最小的那一个。最大公约数记为(a,b),最小公倍数是已知几个数的公倍数,且是最小的那一个。1.法一:辗转相除法 #include<stdio.h>intmain(){inta,b;......
  • P3911 最小公倍数之和
    原题链接《一道思维题(小trick)》\[ans=\sum_{i=1}^{n}\sum_{j=1}^{n}lcm(a_i,a_j)\]当然正常莫反不能是这种形式的,可以观察到\(a_i\)的值域很小,只有\(5\times10^4\),所以我们去考虑直接枚举。$\quad$记\(c_{a_i}\)为\(a_i\)在序列中出现的个数,\(N\)为\(a_i\)......
  • 第五章习题3-输入两个正整数m和n,求其最大公约数和最小公倍数
     ......
  • 7-3 sdut-最大公约数和最小公倍数
    给定2个正整数,求它们的最大公约数和最小公倍数,并输出。输入格式:输入有若干组。每组数据,在一行中给出两个正整数M和N(≤1000),中间有1个空格。输出格式:对于每组输入,在一行中顺序输出M和N的最大公约数和最小公倍数,两数字间以1个空格分隔。输入样例:181220153926576......