zoukankan      html  css  js  c++  java
  • [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;
    }
    
  • 相关阅读:
    SharePoint 2013 中的SQL Server 安全
    SharePoint 2013 的HTML5特性之响应式布局
    SharePoint 2013 一些小技巧
    SharePoint 2013 排错之"Code blocks are not allowed in this file"
    SharePoint 2013 创建搜索中心及搜索设置
    SharePoint 2013 使用PowerShell创建State Service
    SharePoint 2013 内容部署功能简介
    SharePoint 使用PowerShell恢复误删的网站集
    SharePoint 自定义WebPart之间的连接
    linux之misc及使用misc创建字符设备
  • 原文地址:https://www.cnblogs.com/AWhiteWall/p/15144054.html
Copyright © 2011-2022 走看看