ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

luogu P6405 [COCI2014-2015#2] ŠUMA

2021-01-22 22:35:08  阅读:164  来源: 互联网

标签:frac int luogu ll COCI2014 fa maxn 2015 return


题面传送门
这题十分卡精度,我开到了\(10^{-12}\)才过去。
主要有两种做法。
两种做法的核心都是一样的,就是求出相邻两棵树在什么时候一样高。建权值为同样高时间的边,然后找出最大的连通块使的边权相同。
但是在找的过程中我们发现有一些树可能初始高度一样且生长速度一样,那么这些树在任何时候都相同高。在处理这一块上衍生出了两种做法。
做法一:可撤销并查集。
将任何时候都一样高的边(以下称其为万能边)最先加入并查集,然后枚举每一个出现过的天数,将同是这个天数的边加入并查集,每次找到最大连通块。
注意每一个天数枚举完要撤销。时间复杂度\(O(n^2log^2n)\)
代码实现:

#include<cstdio>
#include<algorithm>
#define d(i,j) ((i-1)*n+j)
#define esp 1e-12
#define max(a,b) ((a)>(b)?(a):(b))
#define abs(x) ((x)>0?(x):-(x))
using namespace std;
int n,m,k,ans=1,cnt,un,wn,lasttop;
struct yyy{int x,y;double z;}s[2000039];
int a[2039][2039],b[2039][2039];
inline bool cmp(yyy x,yyy y){return x.z<y.z;}
inline void swap(int &x,int &y){x^=y^=x^=y;}
struct ques{int x,y,flag;};
struct dsu{
	int fa[600039],w[600039],siz[600039],sh;
	ques st[600039],tmp;
	inline int find(int x){return fa[x]==x?x:find(fa[x]);}
	inline void merge(int x,int y){
		int un=find(x),wn=find(y);
		if(un!=wn){
			if(w[un]<w[wn]) swap(un,wn);
			st[++sh]=(ques){un,wn,w[un]==w[wn]};
			siz[un]+=siz[wn];fa[wn]=un;w[un]+=(w[un]==w[wn]);
		}
	}
	inline void del(){
		tmp=st[sh--];
		fa[tmp.y]=tmp.y;w[tmp.x]-=tmp.flag;siz[tmp.x]-=siz[tmp.y];
	}
}f;
int main(){
	freopen("1.in","r",stdin);
//	freopen("suma.out","w",stdout);
	register int i,j,h;
	scanf("%d",&n);
	for(i=1;i<=n;i++){
		for(j=1;j<=n;j++) scanf("%d",&a[i][j]);
	}
	for(i=1;i<=n;i++){
		for(j=1;j<=n;j++) scanf("%d",&b[i][j]);
	}
	for(i=1;i<=n*n;i++) f.fa[i]=i,f.siz[i]=f.w[i]=1;
	for(i=1;i<=n;i++){
		for(j=1;j<=n;j++){
			if(i!=1){
				if(b[i][j]==b[i-1][j]){
					if(a[i][j]==a[i-1][j])f.merge(d(i,j),d(i-1,j));
				} 
				else s[++cnt]=(yyy){d(i,j),d(i-1,j),((double)(a[i-1][j]-a[i][j]))/(b[i][j]-b[i-1][j])};
			} 
			if(s[cnt].z<-esp) cnt--;
			if(j!=1){
				if(b[i][j]==b[i][j-1]){
					if(a[i][j]==a[i][j-1])f.merge(d(i,j),d(i,j-1));
				} 
				else s[++cnt]=(yyy){d(i,j),d(i,j-1),((double)(a[i][j-1]-a[i][j]))/(b[i][j]-b[i][j-1])};
			} 
		    if(s[cnt].z<-esp) cnt--;
		}
	}
	sort(s+1,s+cnt+1,cmp);lasttop=f.sh;
	for(i=1;i<=n*n;i++) ans=max(ans,f.siz[i]);
	for(i=1;i<=cnt;i++){
		for(j=i;j<=cnt;j++){
			if(abs(s[i].z-s[j].z)>esp) break;
			f.merge(s[j].x,s[j].y);
			un=f.siz[f.find(s[j].x)];
			ans=max(ans,un);
		}
		while(f.sh!=lasttop) f.del();
		i=j-1;
	}
	printf("%d\n",ans);
}

做法二:将所有万能边加入并查集缩点,然后同样枚举天数每次进行\(dfs\)查找连通块大小。
时间复杂度\(O(n^2)\),但还没并查集跑得快。
代码实现(放一下zjdl的代码):

#include<cstdio>
#include<algorithm>
#define ll long long
#define maxn 2001
using namespace std;
int n;
struct frac{
	int x,y;//x/y
	frac(int x=0,int y=1):x(x),y(y){}
	friend bool operator == (const frac &a,const frac &b){
		return (ll)a.x*b.y==(ll)b.x*a.y;
	}
	friend bool operator < (const frac &a,const frac &b){
		if((ll)a.y*b.y>0)return (ll)a.x*b.y-(ll)b.x*a.y<0;
		else return (ll)a.x*b.y-(ll)b.x*a.y>0;
	}
};
struct edges{
	int u,v;
	frac k;
}edge[maxn*maxn];
int len;
void add(int u,int v,frac k){
	edge[++len]=(edges){u,v,k};
}
int dx[4]={0,1,0,-1};
int dy[4]={1,0,-1,0};
int id(int x,int y){return (x-1)*n+y;}
int a[maxn][maxn],b[maxn][maxn];
int fa[maxn*maxn],size[maxn*maxn],ksize[maxn*maxn],w[maxn*maxn],tans;
int get(int x){return fa[x]==x?x:fa[x]=get(fa[x]);}
void merge(int x,int y){
	int gx=get(x),gy=get(y);
	if(gx==gy)return;
	size[gy]+=size[gx];
	fa[gx]=gy;
	w[gy]+=w[gx];
	tans=max(tans,w[gy]);
}
int ans;
bool cmp(const edges &x,const edges &y){
	return x.k<y.k;
}
int main(){
	scanf("%d",&n);
	for(int i=1;i<=n;i++)for(int j=1;j<=n;j++)scanf("%d",&b[i][j]);
	for(int i=1;i<=n;i++)for(int j=1;j<=n;j++)scanf("%d",&a[i][j]);
	for(int i=1;i<=n*n;i++)fa[i]=i,size[i]=1;
	for(int i=1;i<=n;i++){
		for(int j=1;j<=n;j++){
			for(int k=0;k<4;k++){
				int ii=i+dx[k],jj=j+dy[k];
				if(a[i][j]==a[ii][jj]&&b[i][j]==b[ii][jj])merge(id(i,j),id(ii,jj));
			}
		}
	}
	for(int i=1;i<=n*n;i++)get(i),ksize[fa[i]]=size[fa[i]];
	for(int i=1;i<=n;i++){
		for(int j=1;j<=n;j++){
			for(int k=0;k<4;k++){
				int ii=i+dx[k],jj=j+dy[k];
				if(ii<1||ii>n||jj<1||jj>n)continue;
				if(fa[id(i,j)]==fa[id(ii,jj)])continue;
				if(a[i][j]==a[ii][jj])continue;
				if((ll)(b[i][j]-b[ii][jj])*(a[ii][jj]-a[i][j])<0)continue;
				add(fa[id(i,j)],fa[id(ii,jj)],frac(b[i][j]-b[ii][jj],a[ii][jj]-a[i][j]));
			}
		}
	}
	sort(edge+1,edge+1+len,cmp);
	for(int i=1;i<=n*n;i++)fa[i]=i,size[i]=1,w[i]=ksize[i],ans=max(ans,w[i]);
	tans=0;
	int l=1;
	for(int i=1;i<=len;i++){
		if(i!=len&&edge[i].k==edge[i+1].k){
			merge(edge[i].u,edge[i].v);
		}
		else{
			ans=max(ans,tans);
			for(int j=l;j<=i;j++){
				fa[edge[j].u]=edge[j].u;
				fa[edge[j].v]=edge[j].v;
				size[edge[j].u]=size[edge[j].v]=1;
				w[edge[j].u]=ksize[edge[j].u];
				w[edge[j].v]=ksize[edge[j].v];
			}
			tans=0;l=i+1;
		}
	}
	printf("%d",ans);
	return 0;
}

标签:frac,int,luogu,ll,COCI2014,fa,maxn,2015,return
来源: https://www.cnblogs.com/275307894a/p/14315742.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有