越来越喜欢AtCoder了,遍地都是神仙题。

题意:

给定一个\(N\)行\(N\)列的迷宫,每一个格子要么是障碍,要么是空地。每一块空地写着一个数码。在迷宫中,每一步只允许向右、向下走,且只能经过空地。

对于每两个连通(从一个可到达另一个)的格子,求出它们数码的乘积。问所有这种乘积的和。

\(1 \leq N \leq 500\)

思路:

容易把到达关系建成一张DAG,我们要做的就只是:对每一个点,求他所有后继的权值和。但是DAG后继数问题,众所周知只有\(O(|E||V|)\)做法,于是换思路。

我们猜测,从一个格子出发,可以达到的点集(即后继集合)大约是一个凸包的形状。

而这个猜想也大约是对的,然而要使其完全正确,还有很长的路要走。

以下用\((i,j)\)表示第\(i\)行第\(j\)列的格子。编号从1开始。

第一步

4177143673

7#########

5#1716155#

6#4#####5#

2#3#597#6#

6#9#8#3#5#

5#2#899#9#

1#6#####6#

6#5359657#

5#########

以上是样例4的网格,由嵌套的3段环状障碍组成。不妨由内而外称为一环,二环,三环。

注意到对于\((3,3)\),他的所有后继在矩形\((3,3) - (9,9)\)里,被三环包围。然而,二环以内的部分却不是\((3,3)\)的后继,需要想办法剔除。

首先发现,如果\((i-1,j)\)和\((i,j-1)\)都是障碍,那么空地\((i,j)\)不会是其他任何格子的后继。因此,如果已经求完了\((i,j)\)自己的后继,完全可以把\((i,j)\)改成障碍。改完之后,可能会产生新的满足同样性质的空地,于是递归地改下去即可。

我们可以考虑从下到上,对每一行分别求答案。每当求完一行所有格子的答案,就把这一行里能改成障碍的都(递归地)修改掉,再进入上一行。

经过这样的操作,就可以证明:

假如第\(i+1\)行到第\(n\)行,都已执行过修改,那么对于空地\((i,j)\),存在一个简单多边形,恰好包含\((i,j)\)的所有后继和若干障碍。

再进一步,这个多边形由一段“上边界”和一段“下边界”拼成,而每一段边界都是仅向下、向右延伸的阶梯型折线。

第二步

我们试着对\((i,j)\),求出他的后继多边形的两段边界。以下给出了求下边界的大致的伪代码。

starting position := (i,j)
while not touching boundary:
while adjacent empty cell exists:
if moving down reaches empty cell:
move down
else:
move right
while (i,j) cannot reach current position:
move right

上边界类似。

如果可以\(O(1)\)地判断,\((i,j)\)是否能够到达\(current position\),那么这段代码的复杂度就是\(O(N)\),总复杂度就是\(O(N^3)\),可过。

第三步

先考虑求下边界时对连通性的判断。

引入“添加”操作,他做的事情是,把某个格子的后继中,所有被修改成障碍的空地还原。此操作的复杂度正比于被还原的格子数量,具体实现见代码中的\(add\)函数。

先清空第\(i\)行及以下的行(都改成障碍),再对第\(i\)行的前\(j\)个格子做添加操作,易证所得的网格满足:

1、\((i,j)\)的所有后继都依然是空地。

2、第\(i\)行下方的所有空地都由第\(i\)行的前\(j\)个格子可达。

3、第一步中的结论依然成立。

注意到先前那段伪代码的流程是:

(1)、沿着\((i,j)\)尽可能向下走,直到无路可走为止。

(2)、从路径尾端不断向右移,直到遇见从\((i,j)\)可达的空地。

(3)、以当前位置为起点,重复(1)。

考虑(2)中遇到的任意一个空地\(P\),则有以下结论(画图后不难理解):

若有\(k \leq j\),\(P\)在以\((i,k)\)为起点的路径上,则此路径必然与一起点为\((i,j)\)的路径相交。

既然有交点,容易发现\(P\)一定由\((i,j)\)可达。于是,若使用以上算法,则(2)中遇到的所有空地都与\((i,j)\)连通。

求下边界解决了,求上边界的话,用对称大法也能同理解决。

细节不少,详见代码。

代码:

#include <bits/stdc++.h>
#pragma GCC optimize(3)
using namespace std;
#define iinf 2000000000
#define linf 1000000000000000000LL
#define ulinf 10000000000000000000ull
#define MOD1 1000000007LL
#define mpr make_pair
typedef long long LL;
typedef unsigned long long ULL;
typedef unsigned long UL;
typedef unsigned short US;
typedef pair < int , int > pii;
clock_t __stt;
inline void TStart(){__stt=clock();}
inline void TReport(){printf("\nTaken Time : %.3lf sec\n",(double)(clock()-__stt)/CLOCKS_PER_SEC);}
template < typename T > T MIN(T a,T b){return a<b?a:b;}
template < typename T > T MAX(T a,T b){return a>b?a:b;}
template < typename T > T ABS(T a){return a>0?a:(-a);}
template < typename T > void UMIN(T &a,T b){if(b<a) a=b;}
template < typename T > void UMAX(T &a,T b){if(b>a) a=b;}
int n,g[505][505],s[505][505],mx[505];
bool e[505][505];
LL res;
int readchar(){
char c=getchar();
while(c==' ' || c=='\n') c=getchar();
if(c=='#') return 0;
return c-'0';
}
void add(int x,int y){
e[x][y]=1;
if(x<n-1 && g[x+1][y] && !e[x+1][y]) add(x+1,y);
if(y<n-1 && g[x][y+1] && !e[x][y+1]) add(x,y+1);
}
int getlower(int x,int y){
int cx=x,cy=y,ret=(y?s[x][y-1]:0);
mx[y]=x;
while(1){
while(1){
if(cx+1<n && e[cx+1][cy]){
++cx;
mx[y]=cx;
ret+=(cy?s[cx][cy-1]:0);
}
else if(cy+1<n && e[cx][cy+1]){
++cy;
}
else break;
}
bool found=0;
while(cy+1<n){
++cy;
if(e[cx][cy]){
found=1;
break;
}
}
if(!found) break;
}
return ret;
}
int getupper(int x,int y){
int cx=x,cy=y,ret=0;
while(1){
while(1){
if(cy+1<n && e[cx][cy+1]){
++cy;
}
else if(cx+1<n && e[cx+1][cy]){
ret+=s[cx][cy];
++cx;
}
else break;
}
bool found=0;
while(cx<n-1){
ret+=s[cx][cy];
++cx;
if(cx>mx[y]) break;
if(e[cx][cy]){
found=1;
break;
}
}
if(!found || cx>mx[y]) break;
}
if(cx<=mx[y]) ret+=s[cx][cy];
return ret;
}
void solveline(int x){
int i,j,k;
memcpy(s,g,sizeof(g));
for(i=0;i<n;++i){
for(j=1;j<n;++j){
s[i][j]+=s[i][j-1];
}
}
for(i=x;i<n;++i) memset(e[i],0,sizeof(e[i]));
for(i=0;i<n;++i){
if(!g[x][i]) continue;
add(x,i);
res-=(LL)g[x][i]*(LL)getlower(x,i);
}
for(i=x;i<n;++i) memset(e[i],0,sizeof(e[i]));
for(i=n-1;i>=0;--i){
if(!g[x][i]) continue;
add(x,i);
res+=(LL)g[x][i]*(LL)(getupper(x,i)-g[x][i]);
}
}
void del(int x,int y){
if((!x || !g[x-1][y]) && (!y || !g[x][y-1])){
g[x][y]=0;
if(x<n-1 && g[x+1][y]) del(x+1,y);
if(y<n-1 && g[x][y+1]) del(x,y+1);
}
}
int main(){
// inputting start
// 数据结构记得初始化! n,m别写反!
int i,j,k;
scanf("%d",&n);
for(i=0;i<n;++i){
for(j=0;j<n;++j){
g[i][j]=readchar();
}
}
#ifdef LOCAL
TStart();
#endif
// calculation start
// 数据结构记得初始化! n,m别写反!
for(i=0;i<n;++i){
for(j=0;j<n;++j){
e[i][j]=(!!g[i][j]);
}
}
for(i=n-1;i>=0;--i){
solveline(i);
for(j=0;j<n;++j){
if(g[i][j]) del(i,j);
}
}
printf("%lld\n",res);
#ifdef LOCAL
TReport();
#endif
return 0;
}

AtCoder AGC028-F:Reachable Cells的更多相关文章

  1. Atcoder abc187 F Close Group(动态规划)

    Atcoder abc187 F Close Group 题目 给出一张n个点,m条边的无向图,问删除任意数量的边后,留下来的最少数量的团的个数(\(n \le 18\) ) 题解 核心:枚举状态+动 ...

  2. AtCoder Grand Contest 002 F:Leftmost Ball

    题目传送门:https://agc002.contest.atcoder.jp/tasks/agc002_f 题目翻译 你有\(n*k\)个球,这些球一共有\(n\)种颜色,每种颜色有\(k\)个,然 ...

  3. 2018年全国多校算法寒假训练营练习比赛(第四场)F:Call to your teacher

    传送门:https://www.nowcoder.net/acm/contest/76/F 题目描述 从实验室出来后,你忽然发现你居然把自己的电脑落在了实验室里,但是实验室的老师已经把大门锁上了.更糟 ...

  4. 牛客练习赛13D:幸运数字Ⅳ(康托展开) F:关键字排序

    链接:https://www.nowcoder.com/acm/contest/70/D 题目: 定义一个数字为幸运数字当且仅当它的所有数位都是4或者7. 比如说,47.744.4都是幸运数字而5.1 ...

  5. 并不对劲的CF1245E&F:Cleaning Ladders

    CF1245 E. Hyakugoku and Ladders 题目大意 有一个10 \(\times\) 10的网格,你要按这样的路径行走: 网格中有一些单向传送门,每个传送门连接的两个格子在同一列 ...

  6. [AtCoder ARC076] F Exhausted?

    霍尔定理 + 线段树? 咱学学霍尔定理... 霍尔定理和二分图完美匹配有关,具体而言,就是定义了二分图存在完美匹配的充要条件: 不妨设当前二分图左端集合为 X ,右端集合为 Y ,X 与 Y 之间的边 ...

  7. Contest2037 - CSU Monthly 2013 Oct (problem F :ZZY and his little friends)

    http://acm.csu.edu.cn/OnlineJudge/problem.php?cid=2037&pid=5 [题解]: 没想通这题暴力可以过.... [code]: #inclu ...

  8. codeforces 277.5 div2 F:组合计数类dp

    题目大意: 求一个 n*n的 (0,1)矩阵,每行每列都只有两个1 的方案数 且该矩阵的前m行已知 分析: 这个题跟牡丹江区域赛的D题有些类似,都是有关矩阵的行列的覆盖问题 牡丹江D是求概率,这个题是 ...

  9. 【ATcoder s8pc_3 F】 寿司

    http://s8pc-3.contest.atcoder.jp/tasks/s8pc_3_f (题目链接) 题意 有一个长度为$N$的数列$A$,初始为$0$.$Q$次操作,每次两个参数$x,y$. ...

随机推荐

  1. February 27 2017 Week 9 Monday

    All the bright precious things fade so fast. 所有的光鲜靓丽都敌不过时间. Try to make some things endurable and et ...

  2. 简单的PHP算法题

    简单的PHP算法题 目录 1.只根据n值打印n个0 2.根据n值打印一行 0101010101010101010101…… 3.根据n值实现1 00 111 0000 11111…… 4.根据n值实现 ...

  3. 设计模式——观察者模式(ObserverPattern)

    观察者模式(ObserverPattern):观察者模式又称发布-订阅(Publish/Subscribe)模式,定义了一个中一对多的依赖关系,让多个观察者对象同时监听某一个主题对象.这个主题对象在状 ...

  4. 关于c++对文件读写的封装

    namespace { UINT_T GetWriteSizeForNoBuf(UINT_T fsize) { UINT_T write_buf_size = ; == ) { write_buf_s ...

  5. “标准查询运算符”是组成语言集成查询 (LINQ) 模式的方法

    “标准查询运算符”是组成语言集成查询 (LINQ) 模式的方法.大多数这些方法都在序列上运行,其中的序列是一个对象,其类型实现了IEnumerable<T> 接口或 IQueryable& ...

  6. 成都夏季招聘会IT行业缺口大!

    上个周末成都的夏季招聘会在新会展中心举行,我们传智播客的专业市场调查员也深入当中.了解IT行业招聘情况,我们发如今IT软件行业专区招聘的公司特别多,可是去应聘的人却非常少.这意味着IT行业正处于供不应 ...

  7. cf 786 B 线段树优化建图

    cf 786 B 链接 CF 思路 n个点,3种建边方式,规模\(O(n^2)\) 线段树优化建图 注意 读入的数据好坑啊,说好的v,u变成了u,v. 两棵树,一棵出,一棵入.线段树的作用只不过是按照 ...

  8. MVC学习七:Razor布局之加载分部视图【PartialView】

    Partial View 顾名思义就是Html代码片段,应用于此HTML代码多次被页面加载时使用.(类似于WebForm程序中的用户控件) 注:PartialView和正常的View页面在访问时没有任 ...

  9. redis的数据结构与命令

    以下部分文档,摘自51cto讲师:汤小洋 redis提供五种数据类型:string,hash,list,set及zset(sorted set). Redis数据就是以key­ value形式来存储的 ...

  10. 促销规则 promotion rule

    参考:http://www.cnblogs.com/winstonyan/archive/2012/10/29/b2c_research_promotion_engine_and_rule_1.htm ...