zoukankan      html  css  js  c++  java
  • [HNOI2016]最小公倍数

    题目

    不难发现题意就是,每条边有两种权值,每次询问两个点(u,v),问(u)(v)是否存在一条路径满足第一类边权的最大值为(a),第二类边权的最大值为(b)

    一个直观的暴力做法就是把(a_ileq a,b_ileq b)的边都加进来,看看加入这些边后(u,v)是否联通;如果联通,在看看(u,v)所在联通块的两种边权的最大值是否分别为(a、b);如果是,答案为Yes,否则为No。显然这个暴力可以使用并查集来实现。

    有了暴力现在开始分块,我们先将所有边按照第一类权值排序,询问按照第二类权值排序;之后分块,对于每个块我们维护一个前缀并查集,我们在处理一组询问之前会将所有第二类权值不超过这个询问的边加入这些前缀并查集中,这样我们的询问就只是查一个前缀,我们暴力把散块里的边加入到上一个并查集里就好了,询问完了再一条一条撤销回来

    时间复杂度是(O((m+q)sqrt{m}log n)),不太会将(log)放进根号里面;经过手玩,块大小在(5sqrt{m})的时候跑得最快

    代码

    #pragma GCC optimize(2)
    #pragma GCC optimize(3)
    #include<bits/stdc++.h>
    #define re register
    const int maxn=5e4+5;
    const int M=1605;
    inline int read() {
    	char c=getchar();int x=0;while(c<'0'||c>'9')c=getchar();
    	while(c>='0'&&c<='9')x=(x<<3)+(x<<1)+c-48,c=getchar();return x;
    }
    struct edge{unsigned short u,v;int x,y;}a[maxn<<1];
    struct Ask{unsigned short s,t,rk;int x,y;}q[maxn];
    int m,B,p[maxn<<1],id[maxn<<1],vx[maxn<<1],vy[maxn<<1],cnt,num,l[M],r[M];
    unsigned short n,Q,Ans[maxn];
    inline int max(int a,int b) {return a>b?a:b;}
    struct Dsu {
    	int C[M],D[M],top,va[maxn],vb[maxn];
    	unsigned short tp,st[M];
    	unsigned short A[M],sz[maxn],B[M],fa[maxn],f[M];
    	std::bitset<M> vis;
    	inline int find(int x) {
    		for(;x!=fa[x];x=fa[x]);return x;
    	}
    	inline int Find(int x) {
    		return x==fa[x]?x:fa[x]=Find(fa[x]);
    	}
    	inline void Merge(const int &x,const int &y,const int &a,const int &b) {
    		int xx=Find(x),yy=Find(y);
    		if(xx==yy) {
    			va[xx]=max(va[xx],a);vb[xx]=max(vb[xx],b);
    			return;
    		}
    		if(sz[xx]<sz[yy])std::swap(xx,yy);
    		if(sz[xx]==sz[yy]) sz[xx]++;fa[yy]=xx;
    		va[xx]=max(va[xx],max(va[yy],a));
    		vb[xx]=max(vb[xx],max(vb[yy],b));
    	}
    	inline void merge(const int &x,const int &y,const int &a,const int &b) {
    		int xx=find(x),yy=find(y);
    		if(xx==yy) {
    			vis[++top]=0;A[top]=xx;
    			C[top]=va[xx],D[top]=vb[xx];
    			va[xx]=max(va[xx],a),vb[xx]=max(vb[xx],b);
    			return;
    		}
    		if(sz[xx]<sz[yy]) std::swap(xx,yy);
    		vis[++top]=1;
    		A[top]=xx,B[top]=yy;fa[yy]=xx;
    		if(sz[xx]==sz[yy]) sz[xx]++,f[top]=1;else f[top]=0;
    		C[top]=va[xx],D[top]=vb[xx];
    		va[xx]=max(va[xx],max(va[yy],a));
    		vb[xx]=max(vb[xx],max(vb[yy],b));
    	}
    	inline void back() {
    		if(vis[top]) fa[B[top]]=B[top];
    		sz[A[top]]-=f[top];
    		va[A[top]]=C[top],vb[A[top]]=D[top];
    		--top;
    	}
    	inline int query(const int &x,const int &y,const int &a,const int &b) {
    		int xx=find(x),yy=find(y);
    		if(xx!=yy) return 0;
    		return va[xx]==a&&vb[xx]==b;
    	}
    	inline void build() {for(re int i=1;i<=n;i++) fa[i]=i,sz[i]=1,va[i]=vb[i]=-1;}
    }G[67];
    inline int cmp(const edge &A,const edge &B) {return A.x<B.x;}
    inline int cop(int A,int B) {return a[A].y<a[B].y;}
    inline int cxp(const Ask &A,const Ask &B) {return A.y<B.y;}
    inline int ask(int *c,int t) {
    	int l=1,r=m,nw=0;
    	while(l<=r) {
    		int mid=l+r>>1;
    		if(c[mid]==t) nw=mid;
    		if(c[mid]<=t) l=mid+1;else r=mid-1;
    	}
    	return nw;
    }
    inline void add(int t) {
    	for(re int i=p[t];i<=num;++i) G[i].Merge(a[t].u,a[t].v,a[t].x,a[t].y);
    }
    int main() {
    	n=read(),m=read();B=5*std::sqrt(m);
    	for(re int i=1;i<=m;i++)a[i].u=read(),a[i].v=read(),a[i].x=read(),a[i].y=read();
    	std::sort(a+1,a+m+1,cmp);
    	for(re int i=1;i<=m;i++)id[i]=i;
    	std::sort(id+1,id+m+1,cop);Q=read();
    	for(re int i=1;i<=m;i++) vx[i]=a[i].x;
    	for(re int i=1;i<=m;i++) vy[i]=a[id[i]].y;
    	for(re int i=1;i<=Q;++i) {
    		q[++cnt].s=read(),q[cnt].t=read(),q[cnt].x=read(),q[cnt].y=read();
    		if(ask(vx,q[cnt].x)==0||ask(vy,q[cnt].y)==0) {
    			--cnt;continue;
    		}
    		q[cnt].rk=i;
    	}
    	int L=1,R;G[0].build();
    	while(L<=m) {
    		R=L+B-1;if(R>m) R=m;++num;
    		for(re int i=L;i<=R;i++) p[i]=num;
    		l[num]=L,r[num]=R;G[num].build();L=R+1;
    	}
    	std::sort(q+1,q+cnt+1,cxp);int lp=1;
    	for(re int i=1;i<=cnt;i++) {
    		while(lp<=m&&vy[lp]<=q[i].y) add(id[lp]),lp++;
    		int pos=ask(vx,q[i].x);
    		if(p[pos+1]!=p[pos]) {
    			Ans[q[i].rk]=G[p[pos]].query(q[i].s,q[i].t,q[i].x,q[i].y);
    			continue;
    		}
    		int u=p[pos]-1,sth=0;
    		for(re int j=l[u+1];j<=pos;j++)
    			if(a[j].y<=q[i].y) ++sth,G[u].merge(a[j].u,a[j].v,a[j].x,a[j].y);
    		Ans[q[i].rk]=G[u].query(q[i].s,q[i].t,q[i].x,q[i].y);
    		while(sth--) G[u].back();
    	}
    	for(re int i=1;i<=Q;i++) puts(Ans[i]?"Yes":"No");return 0;
    }
    
  • 相关阅读:
    我的书单
    写一个小demo过程中遇到的各种问题 学生管理考勤系统(网页模拟)
    高程三 面向对象程序设计
    JavaScript 函数与对象的 简单区别
    高程三 基本包装类型部分的学习
    巨简单巨丑的计算器(写的我快自闭了)
    checked选择器实现tab切换
    JavaScript进行简单的随即验证码生成(适合和我一样刚入门一本完整的教材书都没看完的弟弟)
    dom编程艺术章12
    vue插件开发与发布
  • 原文地址:https://www.cnblogs.com/asuldb/p/12169450.html
Copyright © 2011-2022 走看看