link
P7532 [USACO21OPEN] Balanced Subsets P
sol
我们先解读一下题目中给的限制条件,就是一个凸多边形的形状
单考虑左右边界,发现是先增后减,或者一直往一个方向走
那么我们可以写一个 \(o(n^5)\) 的 DP ,定义 \(F[i][a][b][l(0/1)][r(0/1)]\) 表示
枚举到第 \(i\) 行,现在的左边界是 \(a\),右边界是 \(b\) ,\(l,r\)表示左右边界是否递增,\(0\) 表示增, \(1\)表示减
然后考虑转移
if(x>=l&&y<=r)f[i][l][r][0][0]+=f[i-1][x][y][0][0];
if(x>=l&&x<=r&&y>r)f[i][l][r][0][1]+=f[i-1][x][y][0][0];
if(x>=l&&x<=r&&y>=r)f[i][l][r][0][1]+=f[i-1][x][y][0][1];
if(y>=l&&y<=r&&x<l)f[i][l][r][1][0]+=f[i-1][x][y][0][0];
if(y>=l&&y<=r&&x<=l)f[i][l][r][1][0]+=f[i-1][x][y][1][0];
if(x<=l&&y>=r)f[i][l][r][1][1]+=f[i-1][x][y][1][1];
if(x<l&&y>=r)f[i][l][r][1][1]+=f[i-1][x][y][0][1];
if(x<=l&&y>r)f[i][l][r][1][1]+=f[i-1][x][y][1][0];
if(x<l&&y>r)f[i][l][r][1][1]+=f[i-1][x][y][0][0];
考虑怎么优化
我们发现,一种情况 \(l,r\) 要转移到 \(l',r'\) 在每种 \(0/1\) 状态下都是满足某种关系,那么我们可不可以将他们记录下来呢
答案是可以的
将 \(l,r,l',r'\) 看成 二维平面中的一个点, \(l,r\) 分别对应两个坐标,那么满足条件的 \(l',r'\) 是二维平面中的一些矩阵区域
如图,当 \(0,0\) 状态,也就是两边都要递增是,要满足 \(l'<l,r'<r\) 也就是图中区域
然后用二维前缀和处理就好了
code
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int maxn=155;
const LL TT=1e9+7;
int N,s[maxn];
LL f[maxn][maxn][maxn][2][2],g[maxn][maxn][maxn][2][2];
char ch[maxn];
LL ans=0;
inline int read(){
int ret=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-f;ch=getchar();}
while(ch<='9'&&ch>='0')ret=ret*10+ch-'0',ch=getchar();
return ret*f;
}
int get(int i,int x,int y,int p,int q,int l,int r){
return (((g[i][y][q][l][r]-g[i][x-1][q][l][r])%TT-g[i][y][p-1][l][r])%TT+g[i][x-1][p-1][l][r]+TT)%TT;
}
int main(){
N=read();
for(int i=1;i<=N;i++){
scanf("%s",ch+1);
for(int j=1;j<=N;j++) s[j]=s[j-1]+(ch[j]=='G');
for(int l=N;l>=1;l--)
for(int r=l;r<=N;r++)if(s[r]-s[l-1]==r-l+1){
f[i][l][r][0][0]=1+get(i-1,l,r,l,r,0,0);
f[i][l][r][0][1]=(get(i-1,l,r,r+1,N,0,0)+get(i-1,l,r,r,N,0,1))%TT;
f[i][l][r][1][0]=(get(i-1,1,l-1,l,r,0,0)+get(i-1,1,l,l,r,1,0))%TT;
f[i][l][r][1][1]=((get(i-1,1,l,r,N,1,1)+get(i-1,1,l-1,r+1,N,0,0))%TT+(get(i-1,1,l-1,r,N,0,1)+get(i-1,1,l,r+1,N,1,0))%TT)%TT;
for(int ii=0;ii<=1;ii++)
for(int jj=0;jj<=1;jj++)ans=(ans+f[i][l][r][ii][jj])%TT;
}
for(int p=0;p<=1;p++)
for(int q=0;q<=1;q++)
for(int x=1;x<=N;x++)
for(int y=1;y<=N;y++)
g[i][x][y][p][q]=(((g[i][x-1][y][p][q]+g[i][x][y-1][p][q])%TT-g[i][x-1][y-1][p][q])%TT+f[i][x][y][p][q])%TT;
}
printf("%lld\n",(ans+TT)%TT);
return 0;
}