CodeForces 611 C. New Year and Domino(dp+容斥)

Description
一个只有’.’和’#’组成的h*w的图,一根木棍需要放在两个连续的’.’上,现在有n次查询,每次查询点[r1,c1]到[r2,c2]的矩形区域中有多少种放木棍的方法
Input
第一行两个整数h和w表示矩形行列数,之后为一h*w矩阵,然后是一整数n表示查询数,最后n行每行四个整数r1,c1,r2,c2表示查询区间(1<=h,w<=500,1<=r1<=r2<=h,1<=c1<=c2<=w)
Output
对于每次查询输出点[r1,c1]到[r2,c2]的矩形区域中有多少种放木棍的方法
Sample Input
这里写图片描述
Sample Output
4
0
10
15
Solution
开两个标记数组标记每根木棍的种类,即如果一个点可以作为一个横着放的木棍的左端则做第一类标记,如果可以作为一个竖着放的木棍的上段则做第二类标记,用dp[i][j]表示[1,1]到[i,j]这个矩形区域内被标记点的数目,那么由容斥dp[i][j]=dp[i-1][j]+dp[i][j-1]-dp[i-1][j-1]+第i行和第j列被标记点的数目,每次查询首先得到ans=dp[r2][c2]-dp[r1-1][c2]-dp[r2][c1-1]+dp[r1-1][c1-1],然后将第r2行第c2列上被标记的点数减掉即可
Code

#include
#include
#include
#include
using namespace std;
#define maxn 555
int h,w,n,r1,c1,r2,c2,dp[maxn][maxn],f1[maxn][maxn],f2[maxn][maxn];
char m[maxn][maxn];
int main()
{
    while(~scanf("%d%d",&h,&w))
    {
        memset(dp,0,sizeof(dp));
        memset(f1,0,sizeof(f1));
        memset(f2,0,sizeof(f2));
        memset(m,0,sizeof(m));
        for(int i=1;i<=h;i++)scanf("%s",m[i]+1);
        for(int i=1;i<=h;i++)
            for(int j=1;j<=w;j++)
            {
                if(m[i][j]=='.'&&m[i][j+1]=='.')dp[i][j]++,f1[i][j]=1;
                if(m[i][j]=='.'&&m[i+1][j]=='.')dp[i][j]++,f2[i][j]=1;
                dp[i][j]+=dp[i-1][j]+dp[i][j-1]-dp[i-1][j-1];
            }
        scanf("%d",&n);
        while(n--)
        {
            scanf("%d%d%d%d",&r1,&c1,&r2,&c2);
            int ans=dp[r2][c2]-dp[r1-1][c2]-dp[r2][c1-1]+dp[r1-1][c1-1];
            for(int i=r1;i<=r2;i++)if(f1[i][c2])ans--;
            for(int i=c1;i<=c2;i++)if(f2[r2][i])ans--;
            printf("%d\n",ans);
        }
    }
    return 0;
}

你可能感兴趣的:(Code,Forces,dp,组合数学)