定一个数列,求是否存在连续子列和为m的倍数,存在输出YES,否则输出NO
输入描述
输入文件的第一行有一个正整数T(1≤T≤10),表示数据组数。 接下去有T组数据,每组数据的第一行有两个正整数n,m (1≤n≤100000 ,1≤m≤5000). 第二行有n个正整数x (1≤x≤100)表示这个数列。
输出描述
输出T行,每行一个YES或NO。
输入样例
2 3 3 1 2 3 5 7 6 6 6 6 6
输出样例
YESNO
题解:
预处理前缀和,一旦有两个数模m的值相同,说明中间一部分连续子列可以组成m的倍数。为什么?假设sum[1,i]%m=k,sum[1,j]%m=k,则sum[i+1,j]%m=0。
由鸽巢原理,可以知道一旦n>m,则必定会有某两个前缀和对m取模相等。
复杂度O(n).
抽屉定理:n+1个物品放进n个抽屉里,必定会有一个抽屉有两个物品。这道题相当于把n个物品放进m个抽屉里,如果n>m,那么一定会出现两个前缀和%m==k那么就输出yes。所以n>m直接输出yes。
#include<bits/stdc++.h> using namespace std; int vis[50005]; int sum[100005]; int main() { int t; scanf("%d",&t); while(t--) { int n,m; scanf("%d %d",&n,&m); memset(sum,0,sizeof(sum)); memset(vis,0,sizeof(vis)); for(int i=1;i<=n;i++) { int x; scanf("%d",&x); sum[i]=sum[i-1]+x; vis[sum[i]%m]++; } if(vis[0]>=1||n>m) printf("YES\n"); else { int flag=0; for(int i=1;i<=m;i++) { if(vis[i]>=2) { flag=1; break; } } if(flag) printf("YES\n"); else printf("NO\n"); } } }