【题解】P8865 [NOIP2022] 种花(二分答案,前缀和)
【题解】P8865 [NOIP2022] 种花
场外 VP 选手。唯一场切的一道题,写篇题解纪念一下。(
顺便提一嘴:e 我是真的菜,,其他人&题解这道题都是
题目链接
题意概述
有一个
有两种种花方案。
第一种是
如果存在
第二种是
如果存在
求给定网格图有多少种
答案输出
数据范围
对于所有数据,保证:
测试点编号 | 特殊性质 | 测试点分值 | ||||
---|---|---|---|---|---|---|
无 | ||||||
无 | ||||||
无 | ||||||
无 | ||||||
A | ||||||
B | ||||||
无 | ||||||
无 | ||||||
无 | ||||||
无 | ||||||
无 | ||||||
无 | ||||||
无 | ||||||
无 | ||||||
无 | ||||||
无 |
特殊性质 A:
特殊性质 B:
思路分析
注:我们用
对于这种问题,可以考虑从某一个角度开始来思考它。
首先以
我们可以分别枚举位置
其实此时的种花方案数取决于三点:
- 从
向右有多少个点种花了; - 从
向下走有多少个点 能够成为 形图案的左下角; 形图案的左下角向右有多少个点种花了;
我们定义
我们将从
定义
那么对于一个点
我们发现,对于每一个能成为左下角的点
那么总的答案就是对于所有
可以用前缀和来预处理出来一个
那么我们就可以直接每次用
对于
最后直接枚举每个
时间复杂度
代码实现
//luoguP8865
//A
#include<iostream>
#include<cstdio>
#include<cstring>
#define int long long
using namespace std;
const int mod=998244353;
const int maxn=1005;
int sum1[maxn][maxn],sum2[maxn][maxn],a[maxn][maxn],pos1[maxn][maxn],pos2[maxn][maxn];
int pos[maxn][maxn],cnt[maxn],sum[maxn][maxn],suml[maxn][maxn];
int n,m,c,f;
inline int read()
{
int x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=x*10+ch-48;ch=getchar();}
return x*f;
}
void Clear()
{
memset(sum1,0,sizeof(sum1));
memset(sum2,0,sizeof(sum2));
memset(cnt,0,sizeof(cnt));
memset(sum,0,sizeof(sum));
memset(suml,0,sizeof(suml));
}
signed main()
{
int T,id;
T=read();id=read();
while(T--)
{
n=read();m=read();c=read();f=read();
Clear();
for(int i=1;i<=n;i++)
{
string s;
cin>>s;
for(int j=1;j<=m;j++)
{
a[i][j]=s[j-1]-'0';
sum1[i][j]=sum1[i][j-1]+a[i][j];
}
}
for(int i=1;i<=m;i++)
{
for(int j=1;j<=n;j++)
{
sum2[i][j]=sum2[i][j-1]+a[j][i];
}
}
for(int i=1;i<=n;i++)
{
for(int j=1;j<=m;j++)
{
if(a[i][j]==1){pos1[i][j]=j,pos2[i][j]=i;continue;}
int now=j;
for(int step=(1<<10);step>=1;step>>=1)
{
if(now+step<=m&&sum1[i][now+step]-sum1[i][j]==0)now+=step;
}
pos1[i][j]=now;
now=i;
for(int step=(1<<10);step>=1;step>>=1)
{
if(now+step<=n&&sum2[j][now+step]-sum2[j][i]==0)now+=step;
}
pos2[i][j]=now;
}
}
for(int i=1;i<=m;i++)
{
cnt[i]=1;
for(int j=1;j<=n;j++)
{
(sum[i][j]=sum[i][j-1]+pos1[j][i]-i)%=mod;
(suml[i][j]=suml[i][j-1]+(pos2[j][i]-j)*(pos1[j][i]-i)%mod)%=mod;
}
}
int ansc=0,ansf=0;
for(int i=1;i<=n;i++)
{
for(int j=1;j<=m;j++)
{
if(pos2[i][j]>i+1)(ansc+=(pos1[i][j]-j)*((sum[j][pos2[i][j]]-sum[j][i+1]+mod)%mod)%mod)%=mod;
if(pos2[i][j]>i+1)(ansf+=(pos1[i][j]-j)*((suml[j][pos2[i][j]]-suml[j][i+1]+mod)%mod)%mod)%=mod;
}
}
cout<<ansc*c%mod<<" "<<ansf*f%mod<<'\n';
}
}
虽说我的做法时间复杂度没传统正解优秀,而且思路上被别人说也挺大便的。。但毕竟是我自己想出来的,而且没有写挂一遍 AC,所以还是记录下来吧。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!