1. 程式人生 > >HDU 6416 2018HDU多校賽 第九場 Rikka with Seam(dp + 字首和優化)

HDU 6416 2018HDU多校賽 第九場 Rikka with Seam(dp + 字首和優化)

大致題意:給你一個n*m的01矩陣,現在要讓你每一行和每一列都去掉一個數字,而且要求相鄰兩行之間去掉數字的位置的絕對值要小於等於k。現在問你刪除之後的矩形最多有幾種。

首先,我們一行一行考慮,對於同一行,顯然是看有多少個塊,有多少個塊就有多少個方案。然後對於整個矩陣來說,任意位置(i,j)可以從上一行的(j-k,j+k)之間轉移過來。dp[i][j]表示不考慮重複的情況下,處理到第i行,且第i行刪掉第j個位置的方案數。那麼,顯然根據之前說的轉移區間,有轉移方程:

                                          \large dp[i][j]=\sum_{p=max(1,j-k)}^{min(m,j+k)}dp[i-1][p]

但是,這裡面會有重複,因為一塊裡面刪掉任意一個都是一樣的。所以我們考慮要刪掉這些計算重複的。那麼這重複的具體來說是多少呢?我們不妨設重複的為ss[i][j],表示第i行第j個位置,與其同一行的第j-1個位置重複的部分。我們考慮,兩個相鄰的位置j和j-1的相交區間是[j-k,j-1+k],於是我們這個ss[i][j]也要從上一行的這一個區間轉移過來,當然了,還要保證j和j-1要在同一個塊,否則j和j-1是不會有重複的。具體來說:

                       \large ss[i][j]=\sum_{p=max(1,j-k)}^{min(m,j-1+k)}ss[i-1][p] \quad (str[i][j]!=str[i][j-1])

這樣,我們就解決的這個問題,對於位置(i,j)來說,處理到第i行,且第i行刪掉j的方案數就是dp[i][j]-ss[i][j]。

這個dp暴力的話,時間複雜度是O(N^3)的,但是這個顯然是可以用字首和優化一下。如此複雜度可以到O(N^2),然後為了節省空間,我的程式碼裡面用了滾動陣列。具體見程式碼:

#include<bits/stdc++.h>
#define LL long long
#define mod 998244353
#define pb push_back
#define lb lower_bound
#define ub upper_bound
#define INF 0x3f3f3f3f
#define sf(x) scanf("%d",&x)
#define sc(x,y,z) scanf("%d%d%d",&x,&y,&z)
#define clr(x,n) memset(x,0,sizeof(x[0])*(n+5))
#define IO ios::sync_with_stdio(0);cin.tie(0);cout.tie(0)
#define file(x) freopen(#x".in","r",stdin),freopen(#x".out","w",stdout)
using namespace std;

const int N = 2e3 + 10;

int ss[2][N],s[2][N];
char str[N][N];

int main()
{
    int T; sf(T);
    while(T--)
    {
        int n,m,k;
        sc(n,m,k);
        for(int i=1;i<=n;i++)
        {
            str[i][0]='9';
            scanf("%s",str[i]+1);
        }
        int cur=1,pre=0;
        clr(s[pre],m); clr(ss[pre],m);
        for(int i=1;i<=m;i++)
        {
            s[0][i]=1; ss[0][i]=(str[1][i]==str[1][i-1]);
        }
        for(int i=2;i<=n;i++)
        {
            clr(ss[cur],m); clr(s[cur],m);
            for(int j=1;j<=m;j++)
            {
                s[pre][j]=(s[pre][j-1]+s[pre][j])%mod;
                ss[pre][j]=(ss[pre][j-1]+ss[pre][j])%mod;
            }
            for(int j=1;j<=m;j++)
            {
                int l=max(1,j-k),r=min(m,j+k);
                s[cur][j]=(s[pre][r]-s[pre][l-1]+mod)%mod;
                s[cur][j]=(s[cur][j]-(ss[pre][r]-ss[pre][l]+mod)%mod+mod)%mod;
                if (str[i][j]!=str[i][j-1]) {ss[cur][j]=0;continue;}
                r=min(m,j+k-1); ss[cur][j]=(s[pre][r]-s[pre][l-1]+mod)%mod;
                ss[cur][j]=(ss[cur][j]-(ss[pre][r]-ss[pre][l]+mod)%mod+mod)%mod;
            }
            swap(cur,pre);
        }
        LL ans=s[pre][1]%mod;
        for(int i=2;i<=m;i++)
            ans=(ans+s[pre][i]-ss[pre][i]+mod)%mod;
        printf("%lld\n",ans);
    }
    return 0;
}