4859: [BeiJing2017]机动训练
Time Limit: 20 Sec Memory Limit: 256 MB
Submit: 105 Solved: 63
[Submit][Status][Discuss]Description
AM 4:45又是晴朗的好天气。AM 5:00不要嘛,再睡一会AM 5:05呜……欺负人——————————————————睡眼朦胧的菜酱(?)已经被二爷拉起来晨跑了。而这个时间你在做什么呢?咳咳,言归正传,最近菜酱的训练遇到了点小麻烦。凌晨绕格瑞赛亚岛跑一圈是基本,在那之后,菜酱还要接受严格的机动训练所谓机动训练,就是在紧急情况(?)下,高速且隐蔽地从位置 X 移动到位置 Y 的训练一般来说, X 和 Y 是根据二爷的心情随机决定的但是由于菜酱已经熟悉了地形,每次总是能找到不太费劲的路径,二爷决定增大难度所谓增大难度,其实就是指定整条路径,这样菜酱就没办法抄近道了当然,由于是为了实战而进行的训练,二爷也不会随便乱指定路径,至少不会故意绕路然后发生的问题就是,如何才能“随机”一整条路径出来二爷统计了全岛所有的合法路径,打算每次在这个表格里随机抽一条出来但是很快二爷发现,许多路径所经过的地形是完全相同的,这类路径的训练会更加有用于是二爷修改了随机策略,地形较为常见的路径权重会变得更大。一次偶然的机会,菜酱看到了二爷的随机策略,并发动技能"过目不忘(?)"记了下来现在你要帮菜酱分析数据。为什么是你?当然是因为否则就会被菜酱爆头(并不)整个岛可以看作一片 n*m 的区域,每个格子有自己的地形。一条路径由一系列八连通的格子组成,两个格子八连通当且仅当这两个格子拥有公共的顶点。定义一条“机动路径”如下:1、它是一条不自交的路径,即路径上任意两个格子都不是同一个2、它的起点和终点处于不同位置,换言之这条路径至少包含 2 个格子3、从起点开始,任何一步只能向不远离终点的方向移动,这里不远离指的是 x 和 y 两个方向都不远离举例说明(字符画需要用等宽字体查看):.....y ...... .---.-++... ---... .-x-.-x+... -x+..y .-+-.---... ---... ..y..图中加号和减号标明了与 x 八连通的所有格子,其中加号是“不远离 y”的方向因此可以看出,如下路径是机动路径:++++++y ......+y .......y+...... .....++. ......+.+...... ..++++.. ...+++..x...... x++..... x+++....而如下路径不是机动路径:../---y .......y .x.|--..... ....../. /..|....... x..../.. ..x....... .--/... .y.需要注意的是,某些不合法的路径甚至比机动路径还要短,这是因为机动路径不是按照长度来定义的。接下来定义一条机动路径的地形,岛上的地形由一个矩阵给出,如.**.*..**..*.**.那么,一条机动路径的地形序列就是它所经过的地形排成一列,如x-.......|...y的地形序列就是".****."(不包含引号)每条机动路径的权重就是与之拥有相同地形序列的机动路径数量之和,例如与这条路径拥有相同地形序列的路径有./-y y... ...x x-. ./-x x... ...y y-./... |... ...| ... /... |... ...| ...|... ... .../ ...| |... ... .../ ...|x... .-x y-/. ...y y... .-y x-/. ...x共 8 条,注意回文时正反算两条,以及自己也算一条。所以这条机动路径的权重是 8,同时所有这 8 条机动路径的权重都是 8。现在你需要统计所有的机动路径权重之和。如果对这种统计方式没有直观的感受,可以查看样例说明。Input
第一行两个整数 n, m,表示岛的大小。接下来 n 行,每行 m 个字符,表示岛的地形。字符集随数据规模给出。对于 100%的数据, 1 ≤ n, m ≤ 30,字符集由大小写字母,数字和"."、 "*"构成Output
仅一个数,表示所有机动路径的权重之和。由于这个数可能很大,你只需要输出它对 1000000009 取模的结果。Sample Input
2 2
.*
*.Sample Output
72
【样例解释 1】
用中括号括起来的一些数对表示一条机动路径,坐标先行后列
地形序列".*": [(1, 1), (1, 2)], [(1, 1), (2, 1)], [(2, 2), (2, 1)], [(2, 2),
(1, 2)],共 4 条,每条权重为 4,计 16
地形序列"*.": [(1, 2), (1, 1)], [(2, 1), (1, 1)], [(2, 1), (2, 2)], [(1, 2),
(2, 2)],共 4 条,每条权重为 4,计 16
地形序列"..": [(1, 1), (2, 2)], [(2, 2), (1, 1)],共 2 条,每条权重为 2,计 4
地形序列"**": [(1, 2), (2, 1)], [(2, 1), (1, 2)],共 2 条,每条权重为 2,计 4
地形序列".*.": [(1, 1), (1, 2), (2, 2)], [(1, 1), (2, 1), (2, 2)], [(2, 2),
(2, 1), (1, 1)], [(2, 2), (1, 2), (1, 1)],共 4 条,每条权重为 4,计 16
地形序列"*.*": [(1, 2), (1, 1), (2, 1)], [(2, 1), (1, 1), (1, 2)], [(1, 2),
(2, 2), (2, 1)], [(2, 1), (2, 2), (1, 2)],共 4 条,每条权重为 4,计 16
共计 16+16+4+4+16+16=72HINT
Source
[Submit][Status][Discuss]
BZOJ体面惨不忍睹,还是上LOJ看吧。
数据范围就是跪着求我们DP,而且本体重点不是时空复杂度而是码长,技巧越好编程复杂度越小。
1.平方处理:类似管道取珠,A的位置和B的位置一起作为状态转移,记忆化。
2.容斥:减小码长的利器,将每个方向的值求出,然后将s和t不在同一行/列的方案数减去在同一行/列的方案数。
1 #include<cstdio> 2 #include<cstring> 3 #include<vector> 4 #include<algorithm> 5 #define rep(i,l,r) for (int i=l; i<=r; i++) 6 using namespace std; 7 8 const int N=35,mod=1e9+9; 9 10 struct pii { int x,y; pii(int x=0,int y=0):x(x),y(y) {} }; 11 12 int n,m,f[N][N][N][N],g[3][3][3][3],ans=0; 13 char map[N][N]; 14 vector<pii> dxy1,dxy2; 15 16 void add(int &x,int v) { x+=v; if(x>=mod)x-=mod; } 17 18 int Dp(int x1,int y1,int x2,int y2) { 19 if(x1<1||x1>n||y1<1||y1>m||x2<1||x2>n||y2<1||y2>m||map[x1][y1]!=map[x2][y2])return 0; 20 if(~f[x1][y1][x2][y2])return f[x1][y1][x2][y2]; 21 f[x1][y1][x2][y2]=1; 22 for (vector<pii>::iterator d1=dxy1.begin(); d1!=dxy1.end(); d1++) 23 for (vector<pii>::iterator d2=dxy2.begin(); d2!=dxy2.end(); d2++) 24 add(f[x1][y1][x2][y2],Dp(x1+d1->x,y1+d1->y,x2+d2->x,y2+d2->y)); 25 return f[x1][y1][x2][y2]; 26 } 27 28 int Solve(int dx1,int dy1,int dx2,int dy2) { 29 if(~g[dx1+1][dy1+1][dx2+1][dy2+1])return g[dx1+1][dy1+1][dx2+1][dy2+1]; 30 dxy1.clear(),dxy2.clear(); 31 rep(i,-1,1){ 32 if(i&&i!=dx1)continue; 33 rep(j,-1,1) 34 if(!(!i&&!j) && !(j&&j!=dy1)) dxy1.push_back(pii(i,j)); 35 } 36 rep(i,-1,1){ 37 if(i&&i!=dx2)continue; 38 for(int j=-1; j<=1; j++) 39 if (!(!i&&!j) && !(j&&j!=dy2)) dxy2.push_back(pii(i,j)); 40 } 41 memset(f,-1,sizeof(f)); int sum=0; 42 rep(x1,1,n) rep(y1,1,m) rep(x2,1,n) rep(y2,1,m) add(sum,Dp(x1,y1,x2,y2)); 43 g[dx1+1][dy1+1][dx2+1][dy2+1]=g[dx2+1][dy2+1][dx1+1][dy1+1]=sum; 44 g[-dx1+1][-dy1+1][-dx2+1][-dy2+1]=g[-dx2+1][-dy2+1][-dx1+1][-dy1+1]=sum; 45 return sum; 46 } 47 48 int Solve(int x,int y) { 49 int sum=0; 50 add(sum,Solve(-1,-1,x,y)); add(sum,Solve(-1,1,x,y)); add(sum,Solve(1,-1,x,y)); add(sum,Solve(1,1,x,y)); 51 add(sum,mod-Solve(-1,0,x,y)); add(sum,mod-Solve(1,0,x,y)); add(sum,mod-Solve(0,-1,x,y)); add(sum,mod-Solve(0,1,x,y)); 52 return sum; 53 } 54 55 int main(){ 56 scanf("%d%d",&n,&m); 57 rep(i,1,n) scanf("%s",map[i]+1); 58 memset(g,-1,sizeof(g)); 59 add(ans,Solve(-1,-1)); add(ans,Solve(-1,1)); add(ans,Solve(1,-1)); add(ans,Solve(1,1)); 60 add(ans,mod-Solve(-1,0)); add(ans,mod-Solve(1,0)); add(ans,mod-Solve(0,-1)); add(ans,mod-Solve(0,1)); 61 printf("%d ",ans); 62 return 0; 63 }