解题报告:luogu P1356
题目链接:P1356 数列的整除性
lhr大佬问我一道题:
数字三角形那个题,求得分个位数的最大值。
然后我竟然秒了。
这个题其实也是一样的。
我们容易想到用\(dp[i][j]\)表示前\(i\)个数\(\bmod k\)为\(j\)的方案有无。
很显然:
\[dp[i][j\pm a[i]\bmod k]=dp[i-1][j]
\]
我们要注意负数取模的处理:
inline int mod(int n,int p)
{
if(n>=0) return n%p;
int g=abs(n)/p;
return (n+g*p+p)%p;
}
然后直接递推就好了。
有序的\(dp\)果然简单。
时间复杂度是\(\mathcal O(Tnk)\),然后就过了。
对于上面哪那一题,可以用\(dp[i][j][k]\)代表第\(i\)行第\(j\)列,个位数为\(k\)的方案。
后来考虑到可以状压,去掉\(k\)那一维。
这个题我们就有了空间\(\mathcal O(n\sqrt{k})\)的做法,即把得数化成\(\sqrt{k}\)进制,然后数组的值进行状压即可。
不过有些难打,就只写了无脑的做法。
\(Code\);
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
#define read(x) scanf("%d",&x)
int t,n,a[10005],k;
int dp[10005][105];
inline int mod(int n,int p)
{
if(n>=0) return n%p;
int g=abs(n)/p;
return (n+g*p+p)%p;
}
int main()
{
read(t);
while(t--)
{
memset(dp,0,sizeof(dp));
read(n),read(k);
for(int i=1;i<=n;i++) read(a[i]);
dp[1][mod(a[1],k)]=1;
for(int i=2;i<=n;i++)
{
for(int j=0;j<=k;j++)
{
if(dp[i-1][j])
{
dp[i][mod(j+a[i],k)]=1;
dp[i][mod(j-a[i],k)]=1;
}
}
}
if(dp[n][0]) printf("Divisible\n");
else printf("Not divisible\n");
}
return 0;
}