题目链接: 04-Tokitsukaze and Multiple
Description
题意:给出n个数和p,求最多可以有多少个不相交的连续子序列和可以整除p
Tokitsukaze has a sequence of length n, denoted by a.
Tokitsukaze can merge two consecutive elements of a as many times as she wants. After each operation, a new element that equals to the sum of the two old elements will replace them, and thus the length of a will be reduced by 1.
Tokitsukaze wants to know the maximum possible number of elements that are multiples of p she can get after doing some operations (or doing nothing) on the sequence a.
Input
- There are several test cases.
- The first line contains an integer T (1 ≤ T ≤ 20), denoting the number of test cases. Then follow all the test cases.
- For each test case, the first line contains two integers n and p (1 ≤ n,p ≤ 105), denoting the length of the sequence and the special number, respectively.
- The second line contains n integers, where the i-th integer ai (1 ≤ ai ≤105) is the i-th element of a.
- It is guaranteed that the sum of n in all test cases is no larger than 106.
Output
For each test case, output in one line the maximum possible number of elements that are multiples of p after doing some operations.
Sample Input
2
5 3
2 1 3 2 1
3 1
123 456 789
Sample Output
3
3
Method
- 其实就是贪心的思想,把n个数看成x根线段,从左至右找线段右端点,符合条件就选该条线段,最后输出选择了的线段的总和;
- 正确也是快速的解法是,用map维护线段的和,当前缀和模p的值在map中已被标记,说明该线段内存在能整除p的子线段,因为是从左至右选,所以这也是最优的子线段;
- 我AC的代码是通过遍历右端点,从右端点开始往左寻找能够整除p的线段,找到的话更新左区间上限,同时ans++,最后也是正解。
据说是因为机器的缘故,我AC的代码,赛后提交TLE了,我人傻了,但是从600ms变成>1000ms属实离谱,蒟蒻再努力吧
Code
详见注释
正解: 用map维护前缀和
#include <bits/stdc++.h>
using namespace std;
#pragma GCC optimize(2)
#define ll long long
const int Max = 1e6+3;
const int mod = 1e9+7;
template<typename T> T gcd(T a, T b) { return b ? gcd(b, a % b) : a; }
int T, n, p, tmp, ans, cnt;
map<int, int> mp;
int main()
{
scanf("%d", &T);
while(T--)
{
scanf("%d%d", &n, &p);
mp[0] = 1;
cnt = ans = 0;
for(int i=0; i<n; i++)
{
scanf("%d", &tmp);
cnt = (cnt+tmp)%p;
if(mp[cnt]){ //如果存在能整除p的线段,则选它
ans++;
cnt = 0;
mp.clear();
mp[0] = 1;
}
else mp[cnt] = 1; //否则更新map
}
printf("%d\n", ans);
}
return 0;
}
蒟蒻AC的代码
#include <iostream>
#include <cstdio>
using namespace std;
#pragma GCC optimize(2)
#define Max 100005
#define ll long long
template<typename T> T gcd(T a, T b) { return b ? gcd(b, a % b) : a; }
int a[Max], T, n, p;
ll dp[Max]; //前缀和数组
int main()
{
scanf("%d", &T);
while(T--)
{
int cnt=0, ans = 0;
scanf("%d%d", &n, &p);
for(int i=1; i<=n; i++)
{
scanf("%d", &a[i]);
a[i] %= p; //缩小a[i]
if(a[i]) dp[i] = dp[i-1] + a[i]; //计算前缀和
else dp[i] = 0;
}
for(int i=1; i<=n; i++) //从左至右找线段右端点
{
if(!dp[i]) { //如果该点是p的倍数,更新左区间
ans++; cnt = i;
}
else { //从线段右端点向左寻找左端点,当线段和满足条件时停止,同时更新左区间
for(int j=i-2; j>=cnt; j--)
{
if((dp[i]-dp[j])%p == 0) {
ans++; cnt=i; break;
}
}
}
}
printf("%d\n", ans);
}
return 0;
}
蒟蒻一只,欢迎指正