[JOISC2014]水筒

Posted skylee的OI博客

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[JOISC2014]水筒相关的知识,希望对你有一定的参考价值。

OJ题号:
  BZOJ4242、AtCoder-JOISC2014E

题目大意:
  给你一个h*w的网格图,每个格子可能是空地、障碍物和建筑物。
  你只可以从空地上走过或者从建筑物中穿过。
  建筑物总共有p个,现在有q组询问,求从建筑物A到建筑物B的过程中在空地上连续走过的最长一段路最短能是多少。
思路:
  为了保证走过的最长路最短,我们可以对所有的建筑物及其路径构造最小生成树。
  正确性显然:为了保证最长路最短,我们要使所有能走的边尽量小,这实际上就是一个贪心,也就是我们所熟知的Kruskal算法。
  题目并没有告诉你总共有哪些边,而原图又是一个网格图,暴力算出所有的边显然会TLE,因此需要考虑如何将所有可能边算出来。
  我们可以对原图进行BFS,首先将所有的建筑物加入到队列中,然后对每个建筑物向外扩展。
  如果当两个建筑物所扩展到的范围出现重叠时,我们就将经由该格点的连接两个建筑物的路径加入边集。
  如果两个建筑物最后扩展到的范围被其它建筑物的范围所阻断,不能直接相连,那么说明这两个点可以经由另一个点中转达到更优。
  然而两个建筑物可能会重叠好几次,这时候并不一定要判重,因为每个格点上最多会被连4条边,整个图就最多有h*w*4条边,事实上远远达不到这个值。
  实践证明用map判重反而比不判重要慢。
  每次还要特判重叠的范围是不是属于同一个建筑物,不然会多加很多边,还会MLE。
  然后跑一遍Kruskal求最小生成树即可。
  最后的询问就相当于树上RMQ,用稀疏表或者树剖之类的数据结构维护一下即可。
  然后交到AtCoder上随随便便拿了Rank1,吊打yutaka1999。
  交到BZOJ上无限RE。
  不放心,觉得是系统环境的问题,用Ubuntu测了一发,还是AC。
  找管理员要数据被告知就是官方数据。
  把C++流读入改成C标准读入就A了。

  1 #include<queue>
  2 #include<vector>
  3 #include<cstdio>
  4 #include<cctype>
  5 #include<algorithm>
  6 
  7 inline int getint() {
  8     register char ch;
  9     while(!isdigit(ch=getchar()));
 10     register int x=ch^0;
 11     while(isdigit(ch=getchar())) x=(((x<<2)+x)<<1)+(ch^0);
 12     return x;
 13 }
 14 inline bool isMasu(const char &ch) {
 15     return ch==.||ch==#;
 16 }
 17 inline char getmasu() {
 18     register char ch;
 19     while(!isMasu(ch=getchar()));
 20     return ch;
 21 }
 22 
 23 const int inf=0x7fffffff;
 24 const int dx[]={1,-1,0,0},dy[]={0,0,1,-1};
 25 const int H=2001,W=2001,P=200001,logP=18,Q=200001;
 26 
 27 int h,w,p;
 28 int map[H][W]={{0}};
 29 
 30 class DisjointSet {
 31     private:
 32         int anc[P];
 33         int find(const int &x) {
 34             return x==anc[x]?x:anc[x]=find(anc[x]);
 35         }
 36     public:
 37         DisjointSet() {
 38             for(register int i=0;i<P;i++) {
 39                 anc[i]=i;
 40             }
 41         }
 42         void Union(const int &x,const int &y) {
 43             anc[find(x)]=find(y);
 44         }
 45         bool isConnected(const int &x,const int &y) {
 46             return find(x)==find(y);
 47         }
 48 };
 49 DisjointSet s;
 50 
 51 struct Edge1 {
 52     int u,v,w;
 53     bool operator < (const Edge1 &another) const {
 54         return w<another.w;
 55     }
 56 };
 57 std::vector<Edge1> e1;
 58 
 59 struct Edge {
 60     int to,w;
 61 };
 62 std::vector<Edge> e[P];
 63 inline void add_edge(const int &u,const int &v,const int &w) {
 64     e[u].push_back((Edge){v,w});
 65 }
 66 
 67 struct State {
 68     int dis,root;
 69 };
 70 State vis[H][W];
 71 
 72 struct Point {
 73     int x,y;
 74     bool onMap() const {
 75         return x&&y&&x<=h&&y<=w;
 76     }
 77 };
 78 std::queue<Point> q;
 79 
 80 inline void bfs() {
 81     for(register int i=1;i<=h;i++) {
 82         for(register int j=1;j<=w;j++) {
 83             if(map[i][j]>0) {
 84                 q.push((Point){i,j});
 85                 vis[i][j]=(State){0,map[i][j]};
 86             } else {
 87                 vis[i][j]=(State){-1,0};
 88             }
 89         }
 90     }
 91     while(!q.empty()) {
 92         const Point a=q.front();
 93         q.pop();
 94         for(register int i=0;i<4;i++) {
 95             Point b=(Point){a.x+dx[i],a.y+dy[i]};
 96             if(!b.onMap()) continue;
 97             if(!~map[b.x][b.y]) continue;
 98             if(vis[b.x][b.y].root) {
 99                 const int &u=vis[a.x][a.y].root,&v=vis[b.x][b.y].root,w=vis[a.x][a.y].dis+vis[b.x][b.y].dis;
100                 if(u==v) continue;
101                 e1.push_back((Edge1){u,v,w});
102             } else {
103                 vis[b.x][b.y]=(State){vis[a.x][a.y].dis+1,vis[a.x][a.y].root};
104                 q.push((Point){b.x,b.y});
105             }
106         }
107     }
108 }
109 
110 inline void kruskal() {
111     std::sort(e1.begin(),e1.end());
112     for(register std::vector<Edge1>::iterator i=e1.begin();i!=e1.end();i++) {
113         const int &u=i->u,&v=i->v,&w=i->w;
114         if(s.isConnected(u,v)) continue;
115         s.Union(u,v);
116         add_edge(u,v,w);
117         add_edge(v,u,w);
118     }
119     e1.clear();
120 }
121 
122 class SparseTable {
123     private:
124         int dep[P];
125         int anc[P][logP],max[P][logP];
126         int log2(const float &x) const {
127             return ((unsigned&)x>>23&255)-127;
128         }
129         void dfs(const int &x) {
130             dep[x]=dep[anc[x][0]]+1;
131             for(int i=1;i<=log2(dep[x]);i++) {
132                 anc[x][i]=anc[anc[x][i-1]][i-1];
133                 max[x][i]=std::max(max[x][i-1],max[anc[x][i-1]][i-1]);
134             }
135             for(std::vector<Edge>::iterator i=e[x].begin();i!=e[x].end();i++) {
136                 const int &y=i->to;
137                 if(y==anc[x][0]) continue;
138                 anc[y][0]=x;
139                 max[y][0]=i->w;
140                 dfs(y);
141             }
142             e[x].clear();
143         }
144     public:
145         void init() {
146             for(register int i=1;i<=p;i++) {
147                 if(!dep[i]) {
148                     dfs(i);
149                 }
150             }
151         }
152         int query(int x,int y) const {
153             if(!s.isConnected(x,y)) return -1;
154             int ret=0;
155             while(dep[x]!=dep[y]) {
156                 if(dep[x]<dep[y]) {
157                     std::swap(x,y);
158                 }
159                 for(register int i=log2(dep[x]);i>=0;i--) {
160                     if(dep[anc[x][i]]>=dep[y]) {
161                         ret=std::max(ret,max[x][i]);
162                         x=anc[x][i];
163                     }
164                 }
165             }
166             if(x==y) return ret;
167             for(register int i=log2(dep[x]);i>=0;i--) {
168                 if(anc[x][i]!=anc[y][i]) {
169                     ret=std::max(ret,std::max(max[x][i],max[y][i]));
170                     x=anc[x][i],y=anc[y][i];
171                 }
172             }
173             ret=std::max(ret,std::max(max[x][0],max[y][0]));
174             return ret;
175         }
176 };
177 SparseTable t;
178 
179 int main() {
180     h=getint(),w=getint(),p=getint();
181     const int q=getint();
182     for(register int i=1;i<=h;i++) {
183         for(register int j=1;j<=w;j++) {
184             if(getmasu()==#) map[i][j]=-1;
185         }
186     }
187     for(register int i=1;i<=p;i++) {
188         const int x=getint(),y=getint();
189         map[x][y]=i;
190     }
191     bfs();
192     kruskal();
193     t.init();
194     for(register int i=0;i<q;i++) {
195         printf("%d\n",t.query(getint(),getint()));
196     }
197     return 0;
198 }

 附官方题解:

以上是关于[JOISC2014]水筒的主要内容,如果未能解决你的问题,请参考以下文章

[JOISC2014]バス通学

[JOISC2014]スタンプラリー

JOISC 2014 邮戳拉力赛(基础DP)

JOISC2014K 二人の星座

JOISC2014 たのしい家庭菜園

JOISC 2017 题解