2020 Multi-University Training Contest 3 H / HDU 6794 - Tokitsukaze and Multipl
知识点:1.贪心。2.map维护前缀和。3.dp
分析(map维护前缀和):题目要求的是如果将序列a进行划分,问最多有多少段划分后序列元素和为p的倍数。我们这样考虑,我们用map来维护一段前缀和,如果当前的前缀和在这之前出现过,意味着这两段相等前缀和之间的元素和为p的倍数,这时我们让cnt++,同时把map.clear()掉。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=1e5+10;
int main(){
int t;
scanf("%d",&t);
while(t--){
int n,p,a[maxn],cnt=0,sum=0;
map<int,int> M;
scanf("%d %d",&n,&p);
for(int i=1;i<=n;i++){
scanf("%d",&a[i]);
a[i]=a[i]%p;
}
for(int i=1;i<=n;i++){
sum=(sum+a[i])%p;
if(M.count(sum)==1||a[i]==0||sum==0){ //如果之前出现过相等的sum,或a[i]本身就是p的倍数或sum是p的倍数
cnt++; //个数加一
M.clear(); //将map清空
sum=0; //将前缀和清零
}
else M[sum]++; //否则记录这个sum
}
printf("%d\n",cnt);
}
return 0;
}
/* 2 5 3 2 1 3 2 1 3 1 123 456 789 */
分析(dp):dp[i]=max(dp[i-1],dp[last[cur]]+1),dp[i]表示的是前i个数最多有dp[i]段满足条件,last数组记录的是前缀和cur最近出现的位置。dp[i]要么等于dp[i-1],要么等于dp[last[cur]]+1(last[cur]到i之间的元素和为p的倍数)。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=1e5+10;
int main(){
int t;
scanf("%d",&t);
while(t--){
int m,p,a[maxn],last[maxn],dp[maxn],cur=0;
fill(last,last+maxn,-1);
fill(dp,dp+maxn,0);
last[0]=0;
scanf("%d %d",&m,&p);
for(int i=1;i<=m;i++)
scanf("%d",&a[i]);
for(int i=1;i<=m;i++){
cur=(cur+a[i])%p;
if(last[cur]!=-1) //如果之前出现过cur了
dp[i]=max(dp[i-1],dp[last[cur]]+1);
else
dp[i]=dp[i-1];
last[cur]=i; //更新last数组
}
printf("%d\n",dp[m]);
}
return 0;
}