题解 P1114 【“非常男女”计划】

顾z

2018-09-02 21:00:11

Solution

题目描述:---> [P1114 “非常男女”计划](https://www.luogu.org/problemnew/show/P1114) ## 广告: [安利blog](https://www.luogu.org/blog/RPdreamer/#) **题意概括:** 寻找一段最长的0和1的子序列,要求0和1的数量相同。 **分析:** 很明显,**前缀和问题**。 我们可以将代表女生的值改为-1,这样代表男生的值就是1,1与-1相加即为0,这样如果男生女生人数相同的话,前缀和就是0。 如果直接去写,最普通的写法 O(n^2)? ~~(复杂度不会证明QAQ~~ ```cpp /*60pts*/ for(RI i=1;i<=n;i++) for(RI j=i+1;j<=n;j++) { if(sum[j]-sum[i-1]==0) ans=std::max(ans,j-i+1); } ``` 很不幸,即使吸氧我们也只能能得到70pts. **考虑如何优化**~~(看了一眼题解~~ 存储某个值(前缀和的值)出现过的最左端位置. 再通过枚举去得到我们最长的连续序列, 以样例为例,我们会得到这样的一个前缀和序列↓ 原序列:0 1 0 0 0 1 1 0 0 新序列:-1 0 -1 -2 -3 -2 -1 -2 -3 容易发现 我们的答案是7-1=6 可是如果这个值为负的怎么办? **数组下标不支持负的**想必大家都知道. **怎么办?** 提供解决方案: 1.一位会指针存储负下标的大佬%%%[@花_Q](https://www.luogu.org/space/show?uid=87940) //你可以~~贿赂~~请教他. 2.把数组开的更大一些. //感觉这个很现实吧~~ 我们可以把数组开的更大一些 (由于懒得去判断,所以我直接开了好大的.. 然后在读入的时候再存储一下这个值出现位置 (我们从左到右读入,得到的位置一定是最靠左的. **记得取max!** --------------------代码-------------------- ```cpp #include<bits/stdc++.h> #define IL inline #define RI register int #define m 100008 #define clear(a) memset(a,0,sizeof a) IL void read(int &x){ int f=1;x=0;char s=getchar(); while(s>'9'||s<'0'){if(s=='-')f=-1;s=getchar();} while(s<='9'&&s>='0'){x=x*10+s-'0';s=getchar();} x*=f; } int n,sum[100008],ans,mp[m<<1]; int main() { read(n); for(RI i=1,sex;i<=n;i++) { read(sex); if(sex==0)sex=-1;//将女生的值改为-1,达到两两配对的效果. sum[i]=sum[i-1]+sex; if(!mp[sum[i]+m])mp[sum[i]+m]=i;//上面↑m足够大吧~ } for(RI i=1;i<=n;i++) ans=std::max(ans,sum[i]==0?i:0); //printf("%d",ans); for(RI i=1;i<=n;i++) if(mp[sum[i]+m] and i!=mp[sum[i]+m])//这个and和&&是一样的 ans=std::max(ans,i-mp[sum[i]+m]); printf("%d",ans); } ```