bzoj4793: [CERC2016]Hangar Hurdles

题目

Solution

容易发现,答案是两点间某条路径的每个点可扩展大小的最小值
刚开始我按 k r u s k a l kruskal kruskal重构树模板来写,边权记为两点的可扩展大小的 m i n min min,复杂度 O ( n 2 l o g n + q l o g n ) O(n^2logn+qlogn) O(n2logn+qlogn)发现 T L E TLE TLE
后来发现不需要建那些额外的边,每个点就是一个权值,可我发现 l c a lca lca的预处理还是 O ( n 2 l o g n ) O(n^2logn) O(n2logn),仔细一想,诶, l c a lca lca可以不用预处理,直接查询的时候暴力跑就行了
排序用计数排序优化一下,总复杂度 O ( n 2 + q l o g n ) O(n^2+qlogn) O(n2+qlogn)

Code

T L E TLE TLE代码:

#include<cstdio>
#include<algorithm>
const int N=1002,M=2000002;
struct node{
	int x,y,w;
}e[M];
int n,i,j,t,sum[N][N],g[N][N],rx,ry,fa[M],f[M][22],dep[M],val[M],v[M],m,cnt,c[M][2],q,x1,y1,x2,y2;
inline char gc(){
	static char buf[100000],*p1=buf,*p2=buf;
	return p1==p2&&(p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++;
}
inline int rd(){
	int x=0,fl=1;char ch=gc();
	for (;ch<48||ch>57;ch=gc())if(ch=='-')fl=-1;
	for (;48<=ch&&ch<=57;ch=gc())x=(x<<3)+(x<<1)+(ch^48);
	return x*fl;
}
inline void wri(int a){if(a>=10)wri(a/10);putchar(a%10|48);}
inline int min(int a,int b){return a&((a-b)>>31)|b&(~(a-b)>>31);}
inline int id(int x,int y){return (x-1)*n+y;}
bool cmp(node a,node b){return a.w>b.w;}
int find(int x){return x==fa[x]?x:fa[x]=find(fa[x]);}
int lca(int x,int y){
	if (dep[x]<dep[y]) x^=y,y^=x,x^=y;
	for (int i=21;~i;i--)
		if (dep[f[x][i]]>=dep[y]) x=f[x][i];
	if (x==y) return x;
	for (int i=21;~i;i--)
		if (f[x][i]!=f[y][i]) x=f[x][i],y=f[y][i];
	return f[x][0];
}
int main(){
	n=rd();
	for (i=1;i<=n;i++,gc())
		for (j=1;j<=n;j++) sum[i][j]=sum[i-1][j]+sum[i][j-1]-sum[i-1][j-1]+(gc()=='#');
	for (i=1;i<=n;i++)
		for (j=1;j<=n;j++){
			t=min(min(g[i-1][j-1],g[i-1][j+1]),min(n-i,n-j))+1;
			while (t && sum[i+t-1][j+t-1]-sum[i+t-1][j-t]-sum[i-t][j+t-1]+sum[i-t][j-t]) t--;
			if (!t) continue;
			g[i][j]=t,val[id(i,j)]=t*2-1;
			if (i>1) e[++m]=(node){id(i-1,j),id(i,j),min(val[id(i-1,j)],val[id(i,j)])};
			if (j>1) e[++m]=(node){id(i,j-1),id(i,j),min(val[id(i,j-1)],val[id(i,j)])};
		}
	for (i=1;i<=n*n*2;i++) fa[i]=i;
	std::sort(e+1,e+m+1,cmp);
	cnt=n*n;
	for (i=1;i<=m;i++){
		rx=find(e[i].x),ry=find(e[i].y);
		if (rx!=ry){
			c[++cnt][0]=rx,c[cnt][1]=ry;
			v[cnt]=e[i].w;
			fa[rx]=fa[ry]=f[rx][0]=f[ry][0]=cnt;
		}
	}
	for (j=1;j<22;j++)
		for (i=1;i<=cnt;i++) f[i][j]=f[f[i][j-1]][j-1];
	dep[0]=-1;
	for (i=cnt;i>n*n;i--) dep[c[i][0]]=dep[c[i][1]]=dep[i]+1;
	q=rd();
	for (;q--;) x1=rd(),y1=rd(),x2=rd(),y2=rd(),wri(v[lca(id(x1,y1),id(x2,y2))]),puts("");
}

b z o j bzoj bzoj当前 r a n k 1 rank1 rank1代码

#include<cstdio>
#include<vector>
using namespace std;
const int N=1002,M=2000002;
struct node{
	int x,y,w;
}e[M];
vector<node>st[N];
int n,i,j,t,sum[N][N],g[N][N],fa[M],dep[M],v[M],m,q,x1,y1,x2,y2,k,dx[4]={0,0,1,-1},dy[4]={1,-1,0,0},x,y,ans;
inline char gc(){
	static char buf[100000],*p1=buf,*p2=buf;
	return p1==p2&&(p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++;
}
inline int rd(){
	int x=0,fl=1;char ch=gc();
	for (;ch<48||ch>57;ch=gc())if(ch=='-')fl=-1;
	for (;48<=ch&&ch<=57;ch=gc())x=(x<<3)+(x<<1)+(ch^48);
	return x*fl;
}
inline void wri(int a){if(a>=10)wri(a/10);putchar(a%10|48);}
inline int min(int a,int b){return a&((a-b)>>31)|b&(~(a-b)>>31);}
inline int id(int x,int y){return (x-1)*n+y;}
int find(int x){while (x!=fa[x]) x=fa[x];return x;}
void link(int x,int y,int t){
	x=find(x),y=find(y);
	if (x==y) return;
	if (dep[x]>dep[y]) x^=y,y^=x,x^=y;//如果把这句改成<,下一句改成dep[x]++就会超时,因为根是y,后面的操作只跟y有关 
	if (dep[x]==dep[y]) dep[y]++;
	fa[x]=y,v[x]=t;
}
int main(){
	n=rd();
	for (i=1;i<=n;i++,gc())
		for (j=1;j<=n;j++) sum[i][j]=sum[i-1][j]+sum[i][j-1]-sum[i-1][j-1]+(gc()=='#');
	for (i=1;i<=n;i++)
		for (j=1;j<=n;j++){
			t=min(min(g[i-1][j-1],g[i-1][j+1]),min(n-i,n-j))+1;
			while (t && sum[i+t-1][j+t-1]-sum[i+t-1][j-t]-sum[i-t][j+t-1]+sum[i-t][j-t]) t--;
			if (!t) continue;
			g[i][j]=t,v[id(i,j)]=t*2-1;
			st[v[id(i,j)]].push_back((node){i,j,m});
		}
	for (i=n;i;i--)
		for (j=0;j<st[i].size();j++) e[++m]=(node){st[i][j].x,st[i][j].y,i};
	for (i=1;i<=m;i++){
		fa[id(e[i].x,e[i].y)]=id(e[i].x,e[i].y);
		for (k=0;k<4;k++){
			x=e[i].x+dx[k],y=e[i].y+dy[k];
			if (1<=x && x<=n && 1<=y && y<=n && fa[id(x,y)]) link(id(e[i].x,e[i].y),id(x,y),e[i].w);
		}
	}
	q=rd();
	for (;q--;){
		x1=rd(),y1=rd(),x2=rd(),y2=rd();
		x=id(x1,y1),y=id(x2,y2);
		i=j=0,x1=x,y1=y;
		while (x1!=fa[x1]) x1=fa[x1],i++;
		while (y1!=fa[y1]) y1=fa[y1],j++;
		if (x1!=y1){
			puts("0");
			continue;
		}
		ans=1e9;
		while (x!=y){
			if (i<j) x^=y,y^=x,x^=y,i^=j,j^=i,i^=j;
			ans=min(ans,v[x]);
			x=fa[x],i--;
		}
		wri(ans),puts("");
	}
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值