[NOIP2022] 种花

没进 noip 的场外菜鸡选手。

Solution

可以发现 CF 的相似之处在于左侧都有一条竖线,可以枚举这一条竖线,设它的上顶点向右可以拓展 \(a\) 个格子,下顶点向右可以拓展 \(b\) 个,下顶点向下可以拓展 \(c\) 个( F 可以看作 C 再加一条竖线得到,这里的竖线指的是 C 左侧的竖线)。

那么显然,该竖线对于 C 的贡献就是 \(a\times b\),对于 F 的贡献就是 \(a\times b\times c\)。可以做到预处理 \(O(n^2)\),枚举竖线 \(O(n^3)\)

考虑优化,计算一个上顶点对于答案的贡献,如图:

红色节点最多拓展到绿色节点,那么在我们枚举竖线的过程中,该上顶点对 C 的贡献为 \(\sum a\times b_i\)\(a\sum b_i\),其中 \(i\) 为向下能够拓展到的节点。预处理 \(b_i\) 前缀和。

同理,设一个点能向下拓展 \(c\) 格,枚举上顶点,对 F 的贡献为 \(\sum a\times b_i\times c_i\)\(a\sum b_i\times c_i\)。预处理 \(b_i\times c_i\) 前缀和。

这样,就可以枚举每个点然后 \(O(1)\) 计算贡献了,预处理和计算答案的复杂度均为 \(O(n^2)\)

Code

可能稍微繁琐一点,有注释。

#include<bits/stdc++.h>
#define int long long
#define fi first
#define se second
#define pb push_back
#define mkp make_pair
using namespace std;
using ll=long long;
using pii=pair<int,int>;
using pll=pair<ll,ll>;
using ull=unsigned long long;
inline void read(int &x){
  char ch=getchar();
  int r=0,w=1;
  while(!isdigit(ch))w=ch=='-'?-1:1,ch=getchar();
  while(isdigit(ch))r=(r<<1)+(r<<3)+(ch^48),ch=getchar();
  x=r*w;
}
const int N=1007,mod=998244353;
int n,m,c,f,r[N][N],dn[N][N],sum[N][N],sum2[N][N];
char ok[N][N];
int getc(){
  int ans=0;
  for(int i=1;i<=n;i++)for(int j=1;j<=m;j++)
    if(dn[i][j]>=3&&ok[i][j]=='0')//至少3格
    ans=(ans+(r[i][j]-1)*(sum[i+dn[i][j]-1][j]-sum[i+1][j])%mod)%mod;//注意顶点下方的第一个没有贡献
  return ans;
}
int getf(){
  int ans=0;
  for(int i=1;i<=n;i++)for(int j=1;j<=m;j++)
    if(dn[i][j]>3&&ok[i][j]=='0')//至少4格
    ans=(ans+(r[i][j]-1)*(sum2[i+dn[i][j]-2][j]-sum2[i+1][j])%mod)%mod;
  return ans;
}
void init(){
  memset(r,0,sizeof r);memset(dn,0,sizeof dn);
  for(int i=1;i<=n;i++)for(int j=m;j>=1;j--)
    r[i][j]=ok[i][j]=='1'?0:r[i][j+1]+1;//向右最多拓展几个
  for(int j=1;j<=m;j++)for(int i=n;i>=1;i--)
    dn[i][j]=ok[i][j]=='1'?0:dn[i+1][j]+1;//向下
  //这里拓展的都包含了自己本身,所以调用时要减1
  for(int j=1;j<=m;j++)for(int i=1;i<=n;i++)sum[i][j]=sum[i-1][j]+r[i][j]-1;//前缀和
  for(int j=1;j<=m;j++)for(int i=1;i<=n;i++)sum2[i][j]=sum2[i-1][j]+(r[i][j]-1)*(dn[i][j]-1);
}
void solve(){
  read(n);read(m);read(c);read(f);
  for(int i=1;i<=n;i++)for(int j=1;j<=m;j++)cin>>ok[i][j];
  init();
  printf("%lld %lld\n",getc()*c,getf()*f);
}
main(){
  int T,id;read(T);read(id);
  while(T--)solve();
  return 0;
}
posted @ 2022-11-26 21:36  Epoch_L  阅读(394)  评论(0)    收藏  举报