• [HNOI 2016] 最小公倍数


    题目

    传送门

    前言

    来来回回调了好久… 各种 (mathtt{qqgg}) 的锅层出不穷。最后 (mathtt{T}) 飞了以为是自己常数大,结果是之前调的时候为了方便改了一个循环初始变量 —— 单点询问 (mathcal O(mcdot log n)) 复杂度 ( ext{get})

    解法

    首先有一个暴力:对于每个询问 (i),枚举所有 (ale a_i,ble b_i) 的边加入带权并查集,然后查询 ((u_i,v_i)) 是否连通且连通块的 (max a=a_i,max b=b_i) —— 单点询问 (mathcal O(mcdot alpha (n))) 复杂度 ( ext{get})!(悲

    将边从小到大以 (a) 排序并分块,将询问从小到大以 (b) 排序。对于每个询问 (i),找到一个块 (j),使得在 ([1,j]) 中的块中的边都满足 (ale a_i),并把 (i) 加入集合 (be_j) 中。枚举 (i=[0,cnt])(其中 (cnt) 是块的总数),将属于前 (i) 个块的边从小到大以 (b) 排序。再枚举 (jin be_i)(按 (b) 从小到大的顺序),这样属于前 (i) 个块的边可以用 ( ext{two-pointers}) 的方式加入,复杂度 (mathcal Oleft ((B+m)cdot frac{m}{B}cdot alpha (n) ight)=mathcal Oleft (left(m+ frac{m^2}{B} ight)cdot alpha (n) ight))(等差数列求和)。对于第 (i+1) 块的边就只有暴力判断,但此时的并查集需要带撤销,可能是 (mathcal O(qBcdot log n)) 的?

    至于 "将属于前 (i) 个块的边从小到大以 (b) 排序" 操作可以使用归并排序来减少复杂度。

    解了一下 (B) 的最优取值:(sqrt{frac{m^2}{qcdot log n}})不知道有啥用。

    其实也可以不使用带撤销并查集。加入前 (i) 个块的边后将其缩点,然后对于第 (i+1) 个块的边进行 (mathtt{bfs}) 即可。

    总结

    当需要维护偏序时,可以先分块解决一个偏序。再在某块中暴力计算答案。

    代码

    #include <cstdio>
    #define print(x,y) write(x),putchar(y)
    
    template <class T>
    inline T read(const T sample) {
    	T x=0; char s; bool f=0;
    	while((s=getchar())>'9' or s<'0')
    		f|=(s=='-');
    	while(s>='0' and s<='9')
    		x=(x<<1)+(x<<3)+(s^48),
    		s=getchar();
    	return f?-x:x;
    }
    
    template <class T>
    inline void write(const T x) {
    	if(x<0) {
    		putchar('-'),write(-x);
    		return;
    	}
    	if(x>9) write(x/10);
    	putchar(x%10^48);
    } 
    
    #include <cmath>
    #include <vector>
    #include <iostream>
    #include <algorithm>
    using namespace std;
    
    const int maxn=5e4+5,maxm=1e5+5;
    const int maxb=320;
    
    vector <int> be[maxb];
    int n,m,B,R[maxb],q,ans[maxn];
    struct Edge {
    	int u,v,a,b;
    	
    	bool operator < (const Edge &y) const {
    		return a<y.a;
    	}
    } e[maxm];
    struct Query {
    	int u,v,a,b,id;
    	
    	bool operator < (const Query &y) const {
    		return b<y.b;
    	}
    } s[maxn];
    struct DSU {
    	int tp,f[maxn],rk[maxn];
    	int ma[maxn],mb[maxn];
    	struct store {
    		int U,V,A,B,Rk;
    	} sta[maxm];
    	
    	void init() {
    		for(int i=1;i<=n;++i)
    			f[i]=i,rk[i]=1,
    			ma[i]=mb[i]=-1;
    	}
    	
    	int Find(int x) {
    		return x==f[x]?x:Find(f[x]);
    	}
    	
    	void Merge(int u,int v,int aa,int bb) {
    		u=Find(u),v=Find(v);
    		if(rk[u]<rk[v]) swap(u,v);
    		sta[++tp]=(store){u,v,ma[u],mb[u],rk[u]};
    		if(u^v) {
    			f[v]=u,rk[u]+=rk[v];
    		}
    		ma[u]=max(ma[u],max(ma[v],aa));
    		mb[u]=max(mb[u],max(mb[v],bb));
    	}
    	
    	int FindSet(int x) {
    		return x==f[x]?x:f[x]=FindSet(f[x]);
    	}
    	
    	void Unite(int u,int v,int aa,int bb) {
    		u=FindSet(u),v=FindSet(v);
    		if(rk[u]<rk[v]) swap(u,v);
    		if(u^v) {
    			f[v]=u,rk[u]+=rk[v];
    		}
    		ma[u]=max(ma[u],max(ma[v],aa));
    		mb[u]=max(mb[u],max(mb[v],bb));
    	}
    	
    	void rec() {
    		store t;
    		while(tp) {
    			t=sta[tp--];
    			f[t.V]=t.V,rk[t.U]=t.Rk;
    			ma[t.U]=t.A,mb[t.U]=t.B;
    		}
    	}
    } dsu;
    
    bool cmp(const Edge &x,const Edge &y) {
    	return x.b<y.b;
    }
    
    void Sort(int bl) {
    	static Edge t[maxm];
    	static int i,j,cnt,r;
    	i=cnt=0; r=j=(bl-1)*B;
    	while(i<r and j<R[bl]) {
    		if(e[i+1].b<e[j+1].b)	
    			t[++cnt]=e[++i];
    		else t[++cnt]=e[++j];
    	}
    	while(i<r) t[++cnt]=e[++i];
    	while(j<R[bl]) t[++cnt]=e[++j];
    	for(int i=1;i<=R[bl];++i)
    		e[i]=t[i];
    }
    
    int main() {
    	n=read(9),m=read(9);
    	B=sqrt(1.0*m);
    	int lim=(m-1)/B+1;
    	for(int i=1;i<=m;++i)
    		e[i].u=read(9),e[i].v=read(9),
    		e[i].a=read(9),e[i].b=read(9);
    	sort(e+1,e+m+1);
    	for(int i=1;i<=lim;++i)
    		R[i]=min(m,i*B);
    	q=read(9);
    	for(int i=1;i<=q;++i) {
    		s[i].u=read(9),s[i].v=read(9),
    		s[i].a=read(9),s[i].b=read(9);
    		s[i].id=i;
    	}
    	sort(s+1,s+q+1);
    	for(int i=1;i<=q;++i) {
    		int j=0;
    		while(j<lim and e[R[j+1]].a<=s[i].a)
    			++j;
    		be[j].push_back(i);
    	}
    	for(int i=1;i<=lim;++i)
    		sort(e+(i-1)*B+1,e+R[i]+1,cmp);
    	
    	for(int i=0;i<=lim;++i) {
    		dsu.init(); 
    		
    		int pos=0;
    		for(int J=0;J<be[i].size();++J) {
    			int j=be[i][J];
    			while(pos<R[i] and e[pos+1].b<=s[j].b)
    				++pos,dsu.Unite(e[pos].u,e[pos].v,e[pos].a,e[pos].b);
    			for(int k=i*B+1;k<=R[i+1];++k) {
    				if(s[j].b<e[k].b) break;
    				if(s[j].a>=e[k].a)
    					dsu.Merge(e[k].u,e[k].v,e[k].a,e[k].b);
    			}
    			s[j].u=dsu.Find(s[j].u),s[j].v=dsu.Find(s[j].v);
    			ans[s[j].id]=(s[j].u==s[j].v and dsu.ma[s[j].u]==s[j].a and dsu.mb[s[j].u]==s[j].b);
    			dsu.rec();
    		}
    		if(i^lim) Sort(i+1);
    	}
    	
    	for(int i=1;i<=q;++i)
    		puts(ans[i]?"Yes":"No");
    	return 0;
    }
    
  • 相关阅读:
    [转]Delphi DLL的创建、静态 以及动态调用
    Delphi txt文件的操作(读取、写入)
    为什么要使用动态链接库(DLL)
    TStringGrid 实现下拉框
    Ryzen 4000'Vermeer' CPU和Radeon RX'Big Navi'图形卡
    AMD Ryzen 5000‘Cezanne’APU
    AMD–7nm “Rome”芯片SOC体系结构,支持64核
    ARM Cortex-M嵌入式C基础编程(下)
    ARM Cortex-M嵌入式C基础编程(上)
    基于ARM Cortex-M的SoC存储体系结构和实战
  • 原文地址:https://www.cnblogs.com/AWhiteWall/p/15144054.html
Copyright © 2020-2023  润新知