题解 CF888E 【Maximum Subsequence】

· · 题解

咳咳...这道题的正解是meet-in-middle折半搜索

但是!!我要讲讲大家喜闻乐见的玄学算法:随机化

随机化的代码一般很简短,仅仅是简单的模拟而已。

$A1$:这道题题目简单,数据种类少,数据关联性小,正常遍历数量大,所以考虑随机化优化时间复杂度。 $Q2$:随机化不是很玄吗?$WA$会不会霸屏? $A2$:就一般的随机化而言,$WA$是在所难免的,由于随机化属于骗分技巧,所以可以说你的$rp$和调参水平决定了你的分数。 ------------ 那么这道题怎么随机化? 我们不妨从随机的对象说起:我们问的是选择其中的一些数,使得$mod$后最大,我们就可以像[背包问题的模拟退火解决](https://www.luogu.org/blog/firstfan/SA-solves-Package)(感兴趣的可以看看)那样,随机化选择或者不选。 模拟退火的操作需要产生能量差,所以本题我直接进行基本随机化: >$rand()$重复次数,$rand()$选择个数,$rand()$选择数字。 三重$rand()$的稳定性较低,所以循环常数不能小。但是循环常数大了容易$TLE$,所以调参对于一名合格的~~randomer~~OIer相当重要,这里我开的常数在$50000±3000$,能够保证在500ms左右,正确率$80%-90%$。 ![](https://i.loli.net/2019/03/24/5c973d7c60899.png) Here's my code: ```cpp #include <bits/stdc++.h> #define ri register int #pragma GCC optimize("O3")//O3优化对于平时练习时的卡常有奇效 using namespace std; int read() { int num=0; char c=getchar(); while(!isdigit(c)) { c=getchar(); } while(isdigit(c)) { num=(num<<1)+(num<<3)+(c^48); c=getchar(); } return num; }//快读 const int maxn=20031125; int a[maxn]; bool vis[maxn]; int main() { srand((time)(0)); int n=read(); int mod=read(); for(ri i=1;i<=n;i++) { a[i]=read(); }//输入数据 int rnd=rand()%3000; int ans=0; for(ri i=1;i<=50000+rnd;i++)//随机循环次数 { for(ri j=1;j<=n;j++)//清空vis数组 { vis[j]=0; } for(ri j=1;j<=rand()%n+1;j++)//随机选择个数 { int rd=rand()%n+1;//随机选择数字 if(vis[rd])//剪枝 { continue; } ans=max(ans,(ans+a[rd])%mod);//更新答案 vis[rd]=true;//打标记 } } cout<<ans<<endl;//结束 } ```