ABC_419_E - Subarray Sum Divisibility
ABC_419_E - Subarray Sum Divisibility
挖点性质
发现这个 $ L $ 的长度限制非常像一个滑动的窗口,于是我们假设从 $ x $ 位开始的长度为 $ L $ 的窗口(区间 $ [x,x+L-1] $ )的所有数的和为 $ sum $ ,满足 $ sum=k_{1} \times m $ ,然后考虑往后滑一个位置即转移到区间 $ [x+1,x+L] $ ,在满足题目要求的情况下有 $ sum-a_{x}+a_{x+L}=k_{2} \times m $ ,将 $ sum $ 带入得到 $ a_{x+L}-a_{x}=(k_{2}-k_{1}) \times m $ ,即 $ a_{x+L} \equiv a_{x} (mod \ \ m) $ 。
按照下标模 $ L $ 的值,我们可以把所有数分入 $ L $ 个集合中,其中集合中的所有数应该保证模 $ m $ 同余。
同时把这个余数定义为该集合的集合模。
神秘的预处理
由于我们不能确定一个集合的集合模多少最优,且此处 $ n,m,L $ 都很小,所以直接用 $ s_{i,j} $ 数组预处理出第 $ i $ 个集合的集合模等于 $ j $ 的最小值,这将用在后面的状态转移。
动态规划计算答案
设计转态 $ f_{i,j} $ ,表示前 $ i $ 个集合所有的集合模的总和模 $ m $ 等于 $ j $ 。
暴力枚举新增集合的集合模 $ k $ 进行更新,有状态转移方程:$ \ \(
\) f_{i+1,(j+k)% m}=min(f_{i+1,(j+k)% m},f_{i,j}+s_{i,k}) $ 。
最终答案为 $ f_{L,0} $ 。
解释一下从原问题转换到对集合进行动态规划计算的合理性:在状态转移的过程中相当于不断得尝试用各种合法的集合模去拼凑出一个最小答案 $ f_{L,0} $ 。同时一个长度为 $ L $ 的窗口必定恰好包含所有的集合的集合模,所有当所有 集合模的和模 $ m $ 等于 $ 0 $ 时,所有长度为 $ L $ 的窗口中所有数的和模 $ m $ 必定等于 $ 0 $ 。
时间复杂度:$ O(L \cdot m^{2}) $ 。
code
code
#include <bits/stdc++.h>
#define i8 __int128
#define int long long
#define fuck inline
#define lb long double
using namespace std;
// typedef long long ll;
const int N=505+5,mod=1e9+7;
const int INF=1e9+7;
const int inf=LONG_LONG_MAX/4;
// const int mod1=469762049,mod2=998244353,mod3=1004535809;
// const int G=3,Gi=332748118;
// const int M=mod1*mod2;
fuck int read()
{
int x=0,f=1;
char c=getchar();
while(c<'0'||c>'9'){if(c=='-'){f=-1;}c=getchar();}
while(c>='0'&&c<='9'){x=(x<<3)+(x<<1)+(c-'0');c=getchar();}
return x*f;
}
fuck void write(int x)
{
if(x<0){putchar('-');x=-x;}
if(x>9) write(x/10);
putchar(x%10+'0');
}
int n,m,L;
int a[N];
int s[N][N];
int f[N][N];
fuck void solve()
{
cin>>n>>m>>L;
for(int i=1;i<=n;i++)cin>>a[i];
for(int i=1;i<=n;i++)
for(int j=0;j<m;j++)
s[i%L][j]+=((j-a[i]+m)%m);
memset(f,0x3f3f3f3f,sizeof(f));
f[0][0]=0;
for(int i=0;i<L;i++)
for(int j=0;j<m;j++)
for(int k=0;k<m;k++)
f[i+1][(j+k)%m]=min(f[i+1][(j+k)%m],f[i][j]+s[i][k]);
cout<<f[L][0]<<"\n";
}
signed main()
{
// ios::sync_with_stdio(false);
// cin.tie(0); cout.tie(0);
// int QwQ=read();
// int fuckccf=read();
// while(QwQ--)solve();
solve();
return 0;
}
// 6666 66666 666666
// 6 6 6 6 6
// 6 6 6666 6
// 6 6 6 6 6
// 6666 6 6 6666666
完结收工!!!!!

看完点赞,养成习惯
\(\Downarrow\Downarrow\Downarrow\Downarrow\Downarrow\Downarrow\Downarrow\Downarrow\Downarrow\Downarrow\Downarrow\Downarrow\Downarrow\Downarrow\Downarrow\Downarrow\Downarrow\Downarrow\Downarrow\Downarrow\Downarrow\Downarrow\Downarrow\Downarrow\Downarrow\)

浙公网安备 33010602011771号