题目链接 http://codeforces.com/problemset/problem/611/C
题意:
给定一个矩阵(r*c) ,并且矩阵上有障碍物。r<=500,c<=500
q次询问(q<=1,000,000)
每次询问从(r1,c1)到(r2,c2)上,摆放一个1*2的小长方形,不能放到障碍物上,有多少种摆法。
这道题的关键是找到状态转移方程。
我们如果设dp[i][j]为从(1,1)到(i,j)上的摆法数。
那么利用容斥定理。 dp[i][j] = dp[i][j-1] + dp[i-1][j] -dp[i-1][j-1] + c
其中c为[0,2]的一个数。根据状态转移方程。还要加上如果这个点和左边这个点可放,那么加1,如果这个点和上面这个点可放,再加1。
最后算出dp数组。
当然还有一个难点。
因为题目是求 从(r1,c1)到(r2,c2)上的摆法数,而不是(1,1)到 (r2,c2)的。
最后 答案应该 dp[r2][c2] - dp[r1-1][c1]-dp[r1][c1-1]+dp[r1-1][c1-1]-tks
其中tks应该为边缘的数被加上的,因为我们算的时候,那边已经是格子了,所以左边缘中来自于左边一格的要消去。
上边缘中来自上边缘的要消去。
最后代码如下
#include <set>
#include <cmath>
#include <queue>
#include <stack>
#include <vector>
#include <string>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <functional>
using namespace std;
typedef long long LL;
typedef long long ll;
const int maxn = 5e2+10;
const int modn = 1e9+7;
const int INF = 0x3f3f3f3f;
char str[maxn][maxn];
ll dp[maxn][maxn];
int lef[maxn][maxn];
int u[maxn][maxn];
void show(int a[],int n){
for(int i=0;i<n;i++){
printf("%d ",a[i]);
}printf("\n");
}
int main(){
// freopen("C:\\Users\\lenovo\\Desktop\\data.in","r",stdin);
int n,m;
while(scanf("%d%d",&n,&m)!=EOF){
for(int i=0;i<n;i++){
scanf("%s",str[i]);
}
dp[0][0] = 0;
for(int i=0;i<n;i++){
for(int j=0;j<m;j++){
dp[i][j]= (i<0?0:dp[i-1][j])+(j<0?0:dp[i][j-1])-((i<0||j<0)?0:dp[i-1][j-1]);
if(str[i][j]=='.'){
if(i>=1&&str[i-1][j]=='.'){
dp[i][j]++;
u[i][j] = 1;
}
if(j>=1&&str[i][j-1]=='.'){
dp[i][j]++;
lef[i][j] = 1;
}
}
}
}
int k;
scanf("%d",&k);
while(k--){
int x1,x2,y1,y2;
scanf("%d%d%d%d",&x1,&y1,&x2,&y2);
x1--;
y1--;
x2--;
y2--;
ll ans = dp[x2][y2]-(x1-1>=0?dp[x1-1][y2]:0)-(y1-1>=0?dp[x2][y1-1]:0)+((x1-1>=0&&y1-1>=0)?dp[x1-1][y1-1]:0);
for(int i=x1;i<=x2;i++){
if(lef[i][y1])ans--;
}
for(int i=y1;i<=y2;i++){
if(u[x1][i])ans--;
}
printf("%I64d\n",ans);
}
}
}