CF 1027 E.Inverse Coloring(dp)

题目链接:https://codeforces.com/contest/1027/problem/E

题目大意:

  给一个n*n的矩阵(n<=500),要求将其填满,每一个为黑或白,任意两行或两列,要么完全相同,要么完全相反,并且填充的方格内,任意相同颜色矩形的面积不大于k,求填充方案数。

题目分析:

  很好的一道题目,不难发现,若确定第一行和第一列,整个图形也就定了,并且构成相同颜色的矩形区域,在第一行和第一列部分颜色必然是相同连续的,例如第一行为黑黑黑白白黑,可以将其划分为长度为3,2,1的三个部分,第一列为黑黑白白黑黑可划分为2,2,2三个部分,则整个图形最大面积为3*3=6。我们只需要计算长度为n时,最大连续区域颜色相同的方案,然后枚举长和宽即可。

  用dp[i][j]表示铺到第i个,最长相同的连续段为j的方案数。那么到i,j的方案就有两种,第一种为最后一块的长度为k,即从i-j块直接铺过来,另一种最后一块长度不到k,而之前的最长连续长度为k,则最后一段从i-j开始,到i-1位置。

  因此,dp转移方程为$dp[i][j]= \sum_{k=1}^{j}dp[i-j][k]+\sum_{k=i-j}^{i-1}dp[k][j]$。最后最长连续字段长度为i的方案数即为dp[n][i],因为黑白两种颜色,所以最后答案要乘以2即可。

  

#include<bits/stdc++.h>

using namespace std;
typedef long long ll;
typedef long double ld;
typedef unsigned long long ull;
typedef pair <int,int> pii;
#define rep(i,x,y) for(int i=x;i<y;i++)
#define rept(i,x,y) for(int i=x;i<=y;i++)
#define per(i,x,y) for(int i=x;i>=y;i--)
#define all(x) x.begin(),x.end()
#define pb push_back
#define fi first
#define se second
#define mes(a,b) memset(a,b,sizeof a);
#define mp make_pair
#define dd(x) cout<<#x<<"="<<x<<" "
#define de(x) cout<<#x<<"="<<x<<"\n"
#define debug() cout<<"I love Miyamizu Mitsuha forever.\n"
const int inf=0x3f3f3f3f;
const int maxn=505;
const int mod=998244353;
ll dp[maxn][maxn];
ll len[maxn];
int main()
{
    mes(dp,0);
    ios::sync_with_stdio(false);
    cin.tie(0);
    int n,s;
    cin>>n>>s;
    dp[0][1]=1;
    rept(i,1,n)
    {
        rept(j,1,i)//from 1 to i, the max length of contious block is j
        {
            rep(k,1,j) dp[i][j]=(dp[i][j]+dp[i-j][k])%mod;
            rept(k,i-j,i-1) dp[i][j]=(dp[i][j]+dp[k][j])%mod;
        }
    }
    ll ans=0;
    rept(i,1,n) len[i]=dp[n][i];
    rept(i,1,n)
    {
        rept(j,1,n)
        {
            if(i*j<s) ans+=len[i]*len[j];
            ans%=mod;
        }
    }
    cout<<(ans*2)%mod<<"\n";
    return 0;
}

 

posted @ 2020-02-03 14:59  GGMU  阅读(134)  评论(0编辑  收藏  举报