Maximum Subsequence / CodeForces - 888E

Input
The first line contains two integers n and m (1 ≤ n ≤ 35, 1 ≤ m ≤ 109).
The second line contains n integers a1, a2, ..., an (1 ≤ ai ≤ 109).
Output
Print the maximum possible value of(看右图) .
Sample Input 1
4 4
5 2 4 1
Sample Output
3
Sample Input 1
3 20
199 41 299
Sample Output
19
Note

题意
给n个数,求个区间,使其的和取余m最大,求最大余数
题解
折半枚举
代码
#include<bits/stdc++.h>
using namespace std;
const int N = 18;
int n, m, t = 1, ret;
int a[2 * N], b[1 << N];
int find(int x){//二分
int left = 0, right = t - 1, best = 1 << N;
while (left <= right){
int mid = (left + right) / 2;
if(b[mid] == x){
return x;
}
if(b[mid] > x){
right = mid - 1;
}else{
//当b[mid]<x时才更新best,是因为宁可比x小也不能比其大,这样余数最大化
best = b[mid];
left = mid + 1;
}
}
if(best == 1 << N){//如果没有比x小的数
return b[t - 1];//返回最大的余数
}
return best;
}
int main(){
scanf("%d%d", &n, &m);
for(int i = 0; i < n; i++){
scanf("%d", &a[i]);
a[i] %= m;//每个都这样保存,嗯
}
int half = n / 2;//前一半
for(int mask = 0; mask < (1 << half); mask++){//枚举每一种状态
int tmp = 0;
for(int i = 0; i < half; i++){//每个点搜一遍
if(mask & (1 << i)){//如果i点被选中
tmp = (tmp + a[i]) % m;//把选中的点加上
}
}
b[t++] = tmp;//第t个状态的的余数
}
sort(b, b + t);//排个序
half = n - n / 2;//后一半
for(int mask = 0; mask < (1 << half); mask++){//同上
int tmp = 0;
for(int i = 0; i < half; i++){//同上
if(mask & (1 << i)){//同上
tmp = (tmp + a[i + n / 2]) % m;//同上
}
}
int x = find(m - tmp - 1);
//为了余数最大化,找一个数最接近(m - tmp - 1)稍加证明可知这样余数最大
ret = max(ret, (x + tmp) % m);//找最大余数
}
printf("%d\n", ret);
return 0;
}
PS
为什么find()函数中要写:
if(best == 1 << N){//如果没有比x小的数
return b[t];//返回最大的余数
}
举个栗子
设一个数6, x = 4
当前只有余数3,4,5 3种
经计算发现,此时只有选择最大的余数最后的余数才能最大
没有未来的未来不是我想要的未来

浙公网安备 33010602011771号