原题地址
【题目大意】
给出一幅n个点,m条边的无向图,然后给出q组询问。每组询问给定一个区间[L,R],问[L,R]中有多少点对可以相互到达。可以到达的要求是只能走[L,R]中的点。
不超过5组数据,n,m<=50000,q<=100000。
单点时限:4000ms
内存限制:256MB
【题目分析】
分块+莫队
【解题思路】
将询问排序后分块——
当我们枚举第i个关键点为左端点的时候,同时处理所有左端点在(第i-1个关键点,第i个关键点],右端点在第i个关键点及之后的询问,将这些询问按右端点从小到大排序。假设当前的询问区间是[L,R],则我们先将[第i个关键点,R]的边加进并查集,这个部分可以用启发式合并+路径压缩。然后将[L,第i个关键点)的边用启发式合并加进并查集(不能用路径压缩,因为要撤销)。
如果某个询问没有跨越任何一个关键点,就说明区间内的边数小于 m−−√ ,直接用启发式合并+路径压缩的并查集处理即可。处理完了之后再将修改了的元素暴力初始化。
时间复杂度 O((n+q)mlog(n)−−−−−−−√)
【代码】
#include
#include
#include
#include
using namespace std;
typedef long long LL;
const int MAXN=2e5+10;
const int S=233;
int n,m,q,T;
int tot,tota,qcnt,cnt;
int f[MAXN],st[MAXN],ed[MAXN],pre[MAXN],l[MAXN],r[MAXN];
int stid[S*2+10],sts[S*2+10],stf[S*2+10];
int head[MAXN],headq[MAXN],s[MAXN];
struct Tway
{
int u,v,nex;
};
Tway e[MAXN<<1],a[MAXN<<1],qs[MAXN<<1];
inline void adde(int u,int v)
{
++tot;
e[tot].u=u;e[tot].v=v;
e[tot].nex=head[u];head[u]=tot;
}
inline void adda(int u,int v)
{
++tota;
a[tota].u=u;a[tota].v=v;
}
inline void addway(int u,int v)
{
++qcnt;
qs[qcnt].u=u;qs[qcnt].v=v;
qs[qcnt].nex=headq[u];headq[u]=qcnt;
}
inline void _reset()
{
memset(head,0,sizeof(head));
memset(headq,0,sizeof(headq));
tot=tota=cnt=qcnt=0;
}
inline int findf(int x)
{
return x==f[x]? x : f[x]=findf(f[x]);
}
inline int ofindf(int x)
{
return x==f[x]? x : ofindf(f[x]);
}
int main()
{
freopen("C.in","r",stdin);
freopen("C.out","w",stdout);
scanf("%d",&T);
while(T--)
{
scanf("%d%d%d",&n,&m,&q);
for(int i=1;i<=m;++i)
{
int u,v;
scanf("%d%d",&u,&v);
adde(u,v);adde(v,u);
}
for(int i=1;i<=n;++i)
{
st[i]=tota+1;
for(int j=head[i];j;j=e[j].nex)
adda(i,e[j].v);
ed[i]=tota;
}
for(int i=1;i<=q;++i)
{
scanf("%d%d",&l[i],&r[i]);
pre[i]=-1;
addway(i,headq[ed[r[i]]]);
}
for(int sta=S;sta<=tota;sta+=S)
{
for(int i=1;i<=n;++i)
{
f[i]=i;
s[i]=1;
}
int ans=0;
for(int j=sta+1;sta<=tota;++j)
{
if(a[sta].u<=a[j].v && a[j].v<=a[j].u)
{
int fx=findf(a[j].u),fy=findf(a[j].v);
if(fx!=fy)
{
ans-=(1ll*s[fx]*(s[fx]-1)/2+1ll*s[fy]*(s[fy]-1)/2);
s[fy]+=s[fx];f[fx]=fy;
ans+=1ll*s[fy]*(s[fy]-1)/2;
}
}
for(int k=headq[j];k;k=qs[k].nex)
{
int tmp=st[l[qs[k].v]];
if(tmp<=sta-S || stacontinue;
int tmpans=ans,stn=0;
for(int i=tmp;i<=sta;++i)
{
if(l[qs[k].v]<=a[i].v && a[i].v<=r[qs[k].v])
{
int fx=ofindf(a[i].u),fy=ofindf(a[i].v);
if(fx!=fy)
{
if(s[fx]>s[fy])
swap(fx,fy);
stid[++stn]=fx;stf[stn]=f[fx];sts[stn]=s[fx];
stid[++stn]=fy;stf[stn]=f[fy];sts[stn]=s[fy];
tmpans-=(1ll*s[fx]*(s[fx]-1)/2+1ll*s[fy]*(s[fy]-1));
s[fy]+=s[fx];f[fx]=fy;
tmpans+=1ll*s[fy]*(s[fy]-1)/2;
}
}
}
pre[qs[k].v]=tmpans;
for(int i=stn;i;--i)
{
f[stid[i]]=stf[i];
s[stid[i]]=sts[i];
}
}
}
}
for(int qq=1;qq<=q;++qq)
if(pre[qq]!=-1)
printf("%d\n",pre[qq]);
else
{
int ans=0;
for(int i=l[qq];i<=r[qq];++i)
{
f[i]=i;
s[i]=1;
}
for(int i=l[qq];i<=r[qq];++i)
{
for(int j=head[i];j;j=e[j].nex)
{
if(l[qq]<=e[j].v && e[j].v<=r[qq])
{
int fx=findf(i),fy=findf(e[j].v);
if(fx!=fy)
{
ans-=(1ll*s[fx]*(s[fx]-1)/2+1ll*s[fy]*(s[fy]-1)/2);
s[fy]+=s[fx];f[fx]=fy;
ans+=1ll*s[fy]*(s[fy]-1)/2;
}
}
}
}
printf("%d\n",ans);
}
}
return 0;
}
原题地址
题目描述
Description
给定一张N个顶点M条边的无向图(顶点编号为1,2,…,n),每条边上带有权值。
所有权值都可以分解成 2a×3b 的形式。
现在有q个询问,每次询问给定四个参数u、v、a和b,请你求出是否存在一条顶点u到v之间的路径,使得路径依次经过的边上的权值的最小公倍数为 2a×3b 。
注意:路径可以不是简单路径。
Input
输入文件的第一行包含两个整数N和M,分别代表图的顶点数和边数。接下来M行,每行包含四个整数u、v、a、
b代表一条顶点u和v之间、权值为 2a×3b 的边。接下来一行包含一个整数q,代表询问数。接下来q行,每行包含四
个整数u、v、a和b,代表一次询问。询问内容请参见问题描述。 1≤n,q≤50000、1≤m≤100000、0≤a,b≤109
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排序,每 m−−√ 分为1块。然后对于每一块,我们处理a在该块内的所有询问。假设我们现在处理第i块,我们先将第i块的询问及1至i-1块的边取出,按b排序。然后按顺序加入这些边,同时我们要用带权并查集维护图的连通性以及联通块中的a,b的最大值。对于一个询问,可能还存在一些合法的边在第i块中,但这些边不超过 m−−√ 条,我们可以暴力加入再暴力还原。
时间复杂度: O(mm−−√logm)
【代码】
#include
#include
#include
#include
using namespace std;
const int lim=332;
const int MAXN=1e5+10;
int n,m,qs,cnt,co;
int ans[MAXN],ida[MAXN],idb[MAXN];
int fa[MAXN],d[MAXN],va[MAXN],vb[MAXN];
struct pat
{
int x,y,z;
pat(){}
pat(int _x,int _y,int _z){x=_x,y=_y;z=_z;}
};
pat opt[MAXN];
struct Tway
{
int x,y,a,b;
};
Tway e[MAXN],q[MAXN];
inline bool cmpa(const Tway&A,const Tway&B)
{
return A.aint A,int B)
{
return e[A].bq(int A,int B)
{
return q[A].b<q[B].b;
}
inline int findf(int x)
{
return x==fa[x]? x : findf(fa[x]);
}
inline void merge(int x,int y,int A,int B,int typ)
{
x=findf(x);y=findf(y);
if(x!=y)
{
if(d[x]==d[y])
{
if(typ)
opt[++co]=pat(1,x,d[x]);
d[x]++;
}
if(d[x]y])
swap(x,y);
if(typ)
opt[++co]=pat(0,y,y);
fa[y]=x;
if(va[x]y] && va[y]>A)
{
if(typ)
opt[++co]=pat(2,x,va[x]);
va[x]=va[y];
}
if(vb[x]y] && vb[y]>B)
{
if(typ)
opt[++co]=pat(3,x,vb[x]);
vb[x]=vb[y];
}
}
if(va[x]if(typ)
opt[++co]=pat(2,x,va[x]);
va[x]=A;
}
if(vb[x]if(typ)
opt[++co]=pat(3,x,vb[x]);
vb[x]=B;
}
}
inline void remark()
{
for(int i=co;i>=1;--i)
{
if(!opt[i].x)
fa[opt[i].y]=opt[i].z;
else
if(opt[i].x==1)
d[opt[i].y]=opt[i].z;
else
if(opt[i].x==2)
va[opt[i].y]=opt[i].z;
else
vb[opt[i].y]=opt[i].z;
}
co=0;
}
int main()
{
freopen("BZOJ4537.in","r",stdin);
freopen("BZOJ4537.out","w",stdout);
scanf("%d%d",&n,&m);
// lim=(int)floor(sqrt(m-1));
for(int i=1;i<=m;++i)
{
scanf("%d%d%d%d",&e[i].x,&e[i].y,&e[i].a,&e[i].b);
ida[i]=i;
}
sort(e+1,e+m+1,cmpa);
scanf("%d",&qs);
for(int i=1;i<=qs;++i)
scanf("%d%d%d%d",&q[i].x,&q[i].y,&q[i].a,&q[i].b);
for(int i=0;i<=m;i+=lim)
{
cnt=0;
for(int j=1;j<=qs;++j)
if(q[j].a>=e[i].a && (i+lim>m || q[j].aif(!cnt)
continue;
sort(ida+1,ida+i+1,cmpe);
sort(idb+1,idb+cnt+1,cmpq);
for(int j=1;j<=n;++j)
{
fa[j]=j;d[j]=0;
va[j]=vb[j]=-1;
}
for(int j=1,k=1;j<=cnt;++j)
{
while(k<=i && e[ida[k]].b<=q[idb[j]].b)
{
merge(e[ida[k]].x,e[ida[k]].y,e[ida[k]].a,e[ida[k]].b,0);
++k;
}
co=0;
for(int t=min(i+lim-1,m);t>i;--t)
if(e[t].a<=q[idb[j]].a && e[t].b<=q[idb[j]].b)
merge(e[t].x,e[t].y,e[t].a,e[t].b,1);
int x=findf(q[idb[j]].x),y=findf(q[idb[j]].y);
if(x==y && va[x]==q[idb[j]].a && vb[x]==q[idb[j]].b)
ans[idb[j]]=1;
remark();
}
}
for(int i=1;i<=qs;++i)
(ans[i] ? puts("Yes") : puts("No"));
return 0;
}