区间k问题相关
A
给出一个长度为n的数组a 求出最长的子序列 满足和mod k==0
转跳链接 dp
B
给出一个长度为n的数组a 求出最长的子串 满足和mod k==0
参考AC代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| using namespace std; int n,k; int s1[maxn],s2[maxn]; int main(){ cin>>n>>k; int sum=0; for(int i=1;i<=n;i++){ int x; scanf("%d",&x); sum+=x%k; s1[sum]=i; if(!s2[sum]) s2[sum]=i; } int Max=0; for(int i=0;i<k;i++){ f(s1[i]-s2[i]>Max) Max=s1[i]-s2[i]; } printf("%d",maxx); }
|
思路
由于是连续的子串 我们对前缀和求mod
最后相当于求每个0到k-1的余数 第一次出现和最后一次出现的长度差 维护最大值 这样可以把余数减去 即可以被k整除
s1记录的是最后一次出现的位置 s2记录的是第一次出现的位置
C
给出一个长度为n的数组a 要求从中取出m个数字 要求这m个数字中任意两数的差都是k的倍数
专挑连接 组合数
D
给定一个长度为n的数组a 求有多少个子区间(i,j)满足sumij为k的倍数
参考AC代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| using namespace std; int a[maxn]; LL sum[maxn]; int hashh[maxn]; int main(){ // freopen("input.txt","r",stdin); int n,k; scanf("%d%d",&n,&k); mm(sum,0),mm(hashh,0); for(int i=1;i<=n;i++){ scanf("%d",&a[i]); sum[i]=sum[i-1]+1LL*a[i]; } for(int i=1;i<=n;i++) sum[i]%=k; LL ans=0; for(int i=1;i<=n;i++){ ans+=hashh[sum[i]]; hashh[sum[i]]++; if(sum[i]==0) ans++; } printf("%lld\n",ans); return 0; }
|
思路
(a[i]+a[i+1]+···+a[j])%k==0
(sum[j]-sum[i-1])%k==0
sum[j]%k==sum[i-1]%k
所以问题转换成前缀和取mod k后有多少个两两相同的位置
从i到n 实时维护hash可以把复杂度降到on