洛谷P8226题解

本文同步更新于博客园

前言

最优解 \(rank1\),空间吊打 std。

题解

先求出 \(a_i\) 在模 \(k\) 意义下的前缀和,记为 \(s_i\),顺便用桶记录下每种余数出现的次数。
接下来我们 \(O(n)\) 枚举对每个关卡使用 bomb 的答案,假设枚举到第 \(x\) 关。不难发现,我们不选 \(a_x\)\(s_1,s_2,\cdots,s_{x-1}\),没有影响,仅仅会让 \(s_{x+1},s_{x+2},\cdots s_n\) 都减掉 \(a_x\)。所以对于每个 \(x\),答案为 \(\sum\limits_{i=1}^x[s_i\equiv0(\mathrm{mod}\;k)]+\sum\limits_{i=x+1}^n[s_i\equiv a_x(\mathrm{mod}\;k)]\),用刚才的桶进行计算即可。

注意

  • 循环到 \(n+1\) 是因为要计算不使用 bomb 时的答案。
  • 记得每次更新桶。

Code

#include<bits/stdc++.h>
using namespace std;
const int maxn=1e6+5;
const int maxx=3e5+5;
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<<1)+(x<<3)+(ch^48);
        ch=getchar();
    }
    return x*f;
}//快读
int n,m,k,a[maxx],t[maxn],tmp,res,ans;
bool vis[maxx];
int main()
{
    n=read(),m=read(),k=read();
    for(int i=1;i<=m;i++)
	vis[read()]=1;
    for(int i=1;i<=n;i++)
    {
        a[i]=(a[i-1]+read())%k;//记得取模
	if(vis[i])
	    t[a[i]]++;//用桶记录模k的余数
    }
    for(int i=1;i<=n+1;i++)
    {
	tmp=(a[i]-a[i-1]+k)%k;//计算原来的a[i],但有可能为负数,因此加上k再取模
	if(vis[i])
	    t[a[i]]--;//记得要减掉
	ans=max(ans,res+t[tmp]);
	if(vis[i]&&!a[i])
	    res++;
    }
    printf("%d\n",ans);
    return 0;
}
posted @ 2022-03-20 13:45  Ginger_he  阅读(73)  评论(0编辑  收藏  举报