• 洛谷 P4646


    题面传送门

    一道挺有意思的题(?)

    orz djq yyds %%%%%%%%%%%%%%%%%%

    首先一个很直观的想法是将每个房间看作一个节点,在有墙的房间旁边连边权为 (1) 的边然后 bfs,不过这样实现不太方便,最终也无法较直接地判断每堵墙是否被摧毁,因此这里(djq)提供了一个比较简单的方法。

    我们将每堵墙拆成两个点 (i)(i+m) 分别表示这堵墙分隔的两个区域,我们在这两个点之间连边权为 (1) 的边,而显然有的区域表示的是同一个房间,因此我们还要在它们之间连边权为 (0) 的边。具体来说,对于每个点而言会连出一些边,假设个数为 (c),那么这个点引出的这些边就将平面分成了 (c) 个部分,而这些边总共对应着 (2c) 个点,因此总共会有 (c) 对点表示的是同一个区域,而这些表示相同区域的点表示的射线显然是相邻的,因此我们对于每个点开一个 vector 按顺序记录它连出的边的编号,然后扫一遍在相邻的边表示的区域之间连边,并根据方向判断是 (i) 还是 (i+m),这样即可实现缩点(u1s1 这一段可能讲得不是特别明白,具体看代码罢)

    缩完点之后找出表示外层区域的点,这个可以暴力通过对于每个区域进行一遍 DFS 找出纵坐标最大(当然横坐标也行)的边,那么它上方的区域表示的点就是表示最外侧区域的点,由于此题边权只涉及 (0/1),因此进行 01 bfs 即可求出到每个区域的最短距离。

    时间复杂度线性。

    const int MAXN=1e5;
    const int MAXM=2e5;
    const int MAXV=4e5;
    const int MAXE=2e6;
    const int dx[]={1,0,-1,0};
    const int dy[]={0,1,0,-1};
    int n,m,x[MAXN+5],y[MAXN+5],id[MAXN+5][4],dr[MAXN+5][4];
    int sgn(int x){return (x>0)?1:((!x)?0:-1);}
    int direc(int a,int b){for(int i=0;i<4;i++) if(sgn(x[b]-x[a])==dx[i]&&sgn(y[b]-y[a])==dy[i]) return i;}
    int hd[MAXV+5],to[MAXE+5],nxt[MAXE+5],val[MAXE+5],ec=0;
    void adde(int u,int v,int w){/*printf("%d %d %d
    ",u,v,w);*/to[++ec]=v;val[ec]=w;nxt[ec]=hd[u];hd[u]=ec;}
    int my,ids;bitset<MAXN+5> vis;
    void dfs(int t){
    	if(vis[t]) return;vis.set(t);for(int i=0;i<4;i++) if(id[t][i]){
    		dfs(dr[t][i]);if((i==0||i==2)&&y[t]>my) my=y[t],ids=id[t][i]+m;
    	}
    }
    int dis[MAXV+5];
    void bfs(int s){
    	deque<int> q;dis[s]=0;q.push_back(s);
    	while(!q.empty()){
    		int x=q.front();q.pop_front();//printf("%d
    ",x);
    		for(int e=hd[x];e;e=nxt[e]){
    //			printf("edge %d
    ",e);
    			int y=to[e],z=val[e];
    //			printf("%d %d
    ",y,z);
    			if(dis[y]>dis[x]+z){
    				dis[y]=dis[x]+z;
    				(!z)?(q.push_front(y)):(q.push_back(y));
    			}
    		}
    	}
    //	for(int i=1;i<=m<<1;i++) printf("%d %d
    ",i,dis[i]);
    }
    int main(){
    	scanf("%d",&n);for(int i=1;i<=n;i++) scanf("%d%d",&x[i],&y[i]);
    	scanf("%d",&m);for(int i=1;i<=m;i++){
    		int a,b;scanf("%d%d",&a,&b);
    //		printf("%d %d
    ",direc(a,b),direc(b,a));
    		id[a][direc(a,b)]=id[b][direc(b,a)]=i;
    		dr[a][direc(a,b)]=b;dr[b][direc(b,a)]=a;
    		adde(i,i+m,1);adde(i+m,i,1);
    	} memset(dis,63,sizeof(dis));
    	for(int i=1;i<=n;i++){
    //		printf("Vert %d
    ",i);
    		vector<int> d;
    		for(int j=0;j<4;j++) (id[i][j]&&(d.pb(j),0))/*,printf("%d %d
    ",j,id[i][j])*/;
    		if(!d.empty()){
    			d.pb(d[0]);//printf("%d
    ",d.size());
    			for(int j=0;j+1<d.size();j++){
    				int a=d[j],b=d[j+1];
    //				printf("%d %d
    ",id[i][a],id[i][b]);
    				adde(id[i][a]+m*(a==0||a==3),id[i][b]+m*(b==1||b==2),0);
    				adde(id[i][b]+m*(b==1||b==2),id[i][a]+m*(a==0||a==3),0);
    			}
    		}
    	} vector<int> ans;
    	for(int i=1;i<=n;i++) if(!vis[i]){my=-1;ids=0;dfs(i);bfs(ids);}
    	for(int i=1;i<=m;i++) if(dis[i]==dis[i+m]) ans.pb(i);
    	printf("%d
    ",ans.size());for(int i=0;i<ans.size();i++) printf("%d
    ",ans[i]);
    	return 0;
    }
    
  • 相关阅读:
    验证码生成程序
    会话技术Cookie&Session
    jsp基础、el技术、jstl标签、javaEE的开发模式
    js原生的Ajax
    java基础-反射
    hibernate 联合主键 composite-id
    在ubuntu18 安装nginx过程,以及遇到的错误!
    报错解决:error: this statement may fall through [-Werror=implicit-fallthrough=]
    nginx.conf中关于nginx-rtmp-module配置指令详解
    搭建Nginx+nginx-rtmp-module的hls流媒体服务器并用OBS进行推流
  • 原文地址:https://www.cnblogs.com/ET2006/p/luogu-P4646.html
Copyright © 2020-2023  润新知