cf319.B. Modulo Sum(dp && 鸽巢原理 && 同余模)

B. Modulo Sum
time limit per test
2 seconds
memory limit per test
256 megabytes
input
standard input
output
standard output

You are given a sequence of numbers a1, a2, ..., an, and a number m.

Check if it is possible to choose a non-empty subsequence aij such that the sum of numbers in this subsequence is divisible by m.

Input

The first line contains two numbers, n and m (1 ≤ n ≤ 106, 2 ≤ m ≤ 103) — the size of the original sequence and the number such that sum should be divisible by it.

The second line contains n integers a1, a2, ..., an (0 ≤ ai ≤ 109).

Output

In the single line print either "YES" (without the quotes) if there exists the sought subsequence, or "NO" (without the quotes), if such subsequence doesn't exist.

Sample test(s)
Input
3 5
1 2 3
Output
YES
Input
1 6
5
Output
NO
Input
4 6
3 1 1 3
Output
YES
Input
6 6
5 5 5 5 5 5
Output
YES
Note

In the first sample test you can choose numbers 2 and 3, the sum of which is divisible by 5.

In the second sample test the single non-empty subsequence of numbers is a single number 5. Number 5 is not divisible by 6, that is, the sought subsequence doesn't exist.

In the third sample test you need to choose two numbers 3 on the ends.

In the fourth sample test you can take the whole subsequence.

鸽巢原理,不管怎么排,我们对数列求前缀和,在求模后得到pre1 , pre2 ,pre3 ,……pren,因为n>m,必存在prei = prej (i != j), 然后 (prei - prej) = 0 (mod m) 。

所以n>m时,为O(1)

然后剩下的部分n<=m时,用O(m*m)的dp即可。

然而没有我没有考虑鸽巢原理,用O(n*m)的dp加剪枝也过了,,,,,写dp要养成一个好习惯,那就是用当前的 已知解 去推 未知解 ,(反过来的话会碰上一些意想不到的问题),并且这样会自然形成一个剪枝

#include<bits/stdc++.h>
using namespace std;
typedef long long ll ;
const int M = 1e6 + 10 ;
int vis[1000 + 10] ;
ll pre[M] ;
int n , m ;
int dp[1000 + 10] ;
int d[1000 + 10] ;

int main () {
        scanf ("%d%d" , &n , &m) ;
        bool flag = 0 ;
        for (int i = 1 ; i <= n ; i ++) {
                int x ;
                scanf ("%d" , &x) ;
                x = x % m ;
                if (x == 0) flag = 1 ;
                vis[x] ++ ;
        }
        if (flag) {
                puts ("YES") ;
                return 0 ;
        }
        int tmp = -1 ;
        dp[0] = 1 ;
        while (vis[++tmp] == 0 && tmp <= 1000) ; 
        //printf ("%d : num(%d)
" , tmp , vis[tmp]) ;
        for (int i = 1 ; i <= vis[tmp] ; i ++) {
                int ans = tmp*i%m ;
                if (ans == 0) ans = m ;
                dp[ans] = m ;
        }
        if (dp[m]) {
                puts ("YES") ;
                return 0;
        }
        for (int i = tmp+1 ; i <= m ; i ++) {
                if (vis[i] == 0) continue ;
                //printf ("%d : num(%d)
" , i , vis[i]) ;
                for (int i = 0 ; i <= m ; i ++) d[i] = dp[i] ;
                for (int j = 0 ; j <= m ; j ++) {
                        if (d[j] == 0) continue ;
                        for (int k = 1 ; k <= vis[i] ; k ++) {
                                int ans = (j+i*k)%m ;
                                if (ans == 0) ans = m ;
                                dp[ans] = 1 ;
                        }
                }
                if (dp[m]) {
                        puts ("YES") ;
                        return 0 ;
                }
        }
        //for (int i = 1 ; i <= m ; i ++) printf ("%d " , dp[i]) ; puts ("") ;
        puts ("NO") ;
        return 0 ;
}
原文地址:https://www.cnblogs.com/get-an-AC-everyday/p/4800525.html