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

P3247 [HNOI2016]最小公倍数 题解

时间:2023-02-24 13:57:46浏览次数:39  
标签:qp le 公倍数 题解 询问 dat1 int include P3247

题意简述:

给定一个无向图,边权带两个值 \((a,b)\),给定 \(q\) 次询问,每次询问给定两个点,求两个点直接是否有 \(\max(a)=x\) 且 \(\max(b)=y\) 的简单或非简单路径。

解:

如果是单次询问,可以想到我们把所有 \(a\le x\) 且 \(b\le y\) 的边加入,判断 \((u,v)\) 是否联通且带有 \(a=x\) 和带有 \(b=y\) 的边是否能走到,这可以用并查集解决就行了。

现在问题在处理多次询问。

我们可以把每条边按 \(a\) 不下降排序,询问按 \(y\) 不下降排序。

我们再把边按数量分块,再从小到大枚举块,每次找出所有 \(x\le max_a\) 且 \(x\ge min_a\),\(max_a\) 与 \(min_a\) 是指块内的值,也就是找到所有在块内的询问。

那么对于在这个块前面的边 \(a\) 一定小于等于 \(x\),那么可以对前面的块中所有边按 \(b\) 排序,那么只需要找到 \(b\le y\) 的所有边即可,由于询问的 \(y\) 单调递增,那么枚举前面块的边时可以从上次继承过来。

然后处理块内元素,暴力枚举找合适的边加入即可。

为下一次询问,我们需要撤销所有块内操作,这个直接在修改时用栈维护一下修改的值,然后处理完询问复原即可。

那么块内操作不能路径压缩,否则撤回的复杂度会高达 \(O(n)\),每一次询问撤一次复杂度就为 \(O(n\times q)\)。

最后一步,我们需要判断是否能走到带有 \(a=x\) 与 \(b=y\) 的边,这个好处理,在并查集时记录一下当前集合的最大 \(a\) 与最大 \(b\) 即可。

时间复杂度:\(O(q\times\sqrt n\times\log(\sqrt n))\)。

#include<iostream>
#include<cstdio>
#include<vector>
#include<map>
#include<cmath>
#include<algorithm>
using namespace std;
const int N=2e5+5;
int n,m,k,cnt,siz[N],p[N],ans[N],f[N],fa[N],fb[N];
struct node4
{
	int u,v,data,maxa,maxb;
}qp[N];
struct node
{
	int from,to,dat1,dat2;
}a[N];
int cmp(node fi,node se)
{
	return fi.dat1<se.dat1;
}
int cmp3(node fi,node se)
{
	return fi.dat2<se.dat2;
}
struct node2
{
	int name,u,v,dat1,dat2;
}b[N];
int cmp2(node2 fi,node2 se)
{
	return fi.dat2<se.dat2;
}
int bfind(int x)
{
	if(x==f[x])return x;
	return bfind(f[x]);
}
int afind(int x)
{
	if(x==f[x])return x;
	return f[x]=afind(f[x]);
}
int main()
{
	//freopen("eegcd.in","r",stdin);
	//freopen("eegcd.out","w",stdout);
	scanf("%d%d",&n,&m);
	k=sqrt(m);
	for(int i=1;i<=m;i++)scanf("%d%d%d%d",&a[i].from,&a[i].to,&a[i].dat1,&a[i].dat2);
	int q;
	scanf("%d",&q);
	for(int i=1;i<=q;i++)scanf("%d%d%d%d",&b[i].u,&b[i].v,&b[i].dat1,&b[i].dat2),b[i].name=i;
	sort(a+1,a+1+m,cmp);
	sort(b+1,b+1+q,cmp2);
	for(int i=1;i<=m;i++)p[i]=(i-1)/k+1;
	a[m+1].dat1=-1;
	for(int i=1;(i-1)*k+1<=m;i++)
	{
		int l=(i-1)*k+1,r=min(i*k,m),bef=0;
		for(int j=1;j<=n;j++)f[j]=j,fa[j]=fb[j]=-1;
		for(int j=1;j<=q;j++)
		{
			if(b[j].dat1>=a[l].dat1&&b[j].dat1<=a[r].dat1)
			{
				if(b[j].dat1==a[r].dat1&&a[r+1].dat1==b[j].dat1)continue;
				int cnt=0;
				for(int t=bef+1;t<=l-1&&a[t].dat2<=b[j].dat2;t++)
				{
					fa[afind(a[t].to)]=max(fa[afind(a[t].to)],max(fa[afind(a[t].from)],a[t].dat1));
					fb[afind(a[t].to)]=max(fb[afind(a[t].to)],max(fb[afind(a[t].from)],a[t].dat2));
					f[afind(a[t].from)]=afind(a[t].to);
					bef=t;
				}
				for(int t=l;t<=r&&a[t].dat1<=b[j].dat1;t++)
				{
					if(a[t].dat2<=b[j].dat2)
					{
						qp[++cnt]={bfind(a[t].from),bfind(a[t].to),f[bfind(a[t].from)],fa[bfind(a[t].to)],fb[bfind(a[t].to)]};
						fa[bfind(a[t].to)]=max(fa[bfind(a[t].to)],max(fa[bfind(a[t].from)],a[t].dat1));
						fb[bfind(a[t].to)]=max(fb[bfind(a[t].to)],max(fb[bfind(a[t].from)],a[t].dat2));
						f[bfind(a[t].from)]=f[bfind(a[t].to)];
					}
				}
				if(bfind(b[j].u)==bfind(b[j].v)&&fa[bfind(b[j].u)]==b[j].dat1&&fb[bfind(b[j].u)]==b[j].dat2)ans[b[j].name]=1;
				for(int t=cnt;t>=1;t--)
				{
					fa[qp[t].v]=qp[t].maxa;
					fb[qp[t].v]=qp[t].maxb;
					f[qp[t].u]=qp[t].data;
				}
			}
			
		}
		sort(a+1,a+1+r,cmp3);
	}
	for(int i=1;i<=q;i++)
	{
		if(ans[i])puts("Yes");
		else puts("No");
	}
	return 0; 
}

标签:qp,le,公倍数,题解,询问,dat1,int,include,P3247
From: https://www.cnblogs.com/gmtfff/p/p3247.html

相关文章

  • P2489 [SDOI2011]迷宫探险 题解
    题意简述:一个\(n\timesm\)的带墙体单入口多出口迷宫中有\(k\)个陷阱,陷阱分为有害或无害,有害会使人掉血,给出所有垃圾的有害与无害的所有排列组成的概率,给定人的血量,求......
  • P4416 [COCI2017-2018#1] Plahte 题解
    题意简述:给定\(n\)个互不相交,可以重叠的矩阵,对某些点染色,这个点上的所有矩阵会被染上这个颜色,求最后每个矩阵会有多少种颜色。解:我们可以先把矩阵拆成上下两条水平线......
  • CF1034A Enlarge GCD 题解
    题意简述:删去最少的数使所有的数的\(\text{gcd}\)增加。解:先对每个数除以所有数的\(\text{gcd}\),然后剩下的需要找到所有数的质因子,找到一个最多的序列中数拥有的质......
  • CF436E Cardboard Box 题解
    一道经典的反悔贪心题。考虑每次选择使总星数加一,那么总共有四种情况。一、对于一关没有星,选一颗星,代价为\(a_i\)。二、对于一关有一颗星,再选一颗星,代价为\(b_i-a_i\)......
  • CF204E Little Elephant and Strings 题解
    由于是多个串,还与每个子串的信息有关,很容易想到用SA或广义SAM。这里选择用SA。首先先把字符串转化为数组,连接起来,中间用一些不会出现的数。处理出后缀数组与\(height......
  • P5842 [SCOI2012]Blinker 的仰慕者 题解
    题意简述:求\([l,r]\)内各个数位积为\(k\)的数的和。解:在看题解前,请先确认自己是否精通了数位dp模板题,否则会很难理解。对于朴素的数位dp,我们可以记录\(f_{i,j......
  • P5416 [CTSC2016]时空旅行 题解
    首先题中的\(y,z\)好像没啥用……首先对于每一次询问,要求\(\min((x_0-x_i)^2+c_i)\)设\((x_0-x_i)^2+c_i=ans\)。那么\(x_0^2+x_i^2-2x_0x_i+c_i=ans\)所以\(x_0......
  • P7862 [COCI2015-2016#2] DRŽAVA 题解
    适当进行骗分是真的有用。\(40pts\):对于每两个点建立一条边,然后在贪心每次求最小边,在期间进行01背包即可,01背包用于处理模数。设\(dp_{i,j}\)代表以\(i\)为编号的一......
  • P1379 八数码难题 题解
    IDA*练习题由于题目问最小步数,很好想到可以用迭代式加深搜索,或是广搜,这里用的是深搜。枚举每次搜索的深度,也就是移动的步数,然后正常深搜,若达到目标解,返回\(\text{ture}......
  • P3213 [HNOI2011]勾股定理 题解
    据说是NP问题。很明显我们要先预处理出来勾股数对。但由于数过于大,所以常规的枚举是解决不了问题的。但也貌似没有什么很好的办法可以立马找到一个数的勾股数对。所以......