题意简述:
给定一个无向图,边权带两个值 \((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