从 洛谷P5309 Ynoi2011 初始化 看卡常
一般情况下,程序运行消耗时间主要与时间复杂度有关,超时与否取决于算法是否正确。
但对于某些题目,时间复杂度正确的程序也无法通过,这时我们就需要卡常数,即通过优化一些操作的常数因子减少时间消耗。
比如这道题 P5309 [Ynoi2011] 初始化 。
这道题目的做法我写在另一篇博客里,这里主要研究卡常方式。
#include<bits/stdc++.h>
using namespace std;
const int h=200010;
inline int read() {
int s = 0, w = 1;
char ch = getchar();
while(ch < '0' || ch > '9') {
if(ch == '-') w= -1;
ch = getchar();
}
while(ch >= '0' && ch <= '9') {
s = s * 10 + ch - '0';
ch = getchar();
}
return s * w;
}
int mod=1e9+7;
inline void update(int &x, int y) {
x += y;
if(x >= mod) x -= mod;
} int n,m;
int a[h];
int b_sum[h];
int len;
int pre[2010][2010];
int suf[2010][2010];
int get_pos(int x){
return (x-1)/len+1;
}
int main(){
n=read(),m=read();
len=135;
for(int i=1;i<=n;i++)
a[i]=read(),update(b_sum[get_pos(i)],a[i]);
int op,x,y,z;
for(int i=1;i<=m;i++){
op=read(),x=read(),y=read();
if(op==1){
z=read();
if(x>=len)
for(int j=y;j<=n;j+=x)
update(a[j],z),update(b_sum[get_pos(j)],z);
else{
for(int j=y;j<=x;j++)
update(pre[x][j],z);
for(int j=1;j<=y;j++)
update(suf[x][j],z);
}
}
else{
int l=x,r=y,lb=get_pos(x),rb=get_pos(y);
int ans=0;
if(lb==rb)
for(int j=l;j<=r;j++)
update(ans,a[j]);
else{
for(int j=l;j<=lb*len;j++)
update(ans,a[j]);
for(int j=lb+1;j<=rb-1;j++)
update(ans,b_sum[j]);
for(int j=(rb-1)*len+1;j<=r;j++)
update(ans,a[j]); } for(int j=1;j<len;j++){
lb=(l-1)/j+1,rb=(r-1)/j+1;
if(lb==rb)
ans-=pre[j][(l-1)%j],ans%=mod,ans+=pre[j][(r-1)%j+1],ans%=mod;
else
ans=(ans+1ll*(rb-lb-1)*pre[j][j]+pre[j][(r-1)%j+1]+suf[j][(l-1)%j+1])%mod;
}
ans = ans % mod + mod;
ans = (ans >= mod) ? ans - mod : ans;
printf("%d\n",ans);
}
} return 0;
}
20分超时代码
技巧一:手写常数因子大的运算
c++中取模运算常数因子很大,于是我们改用更快的运算组合表示取模。
inline void update(int &x, int y) {
x += y;
if(x >= mod) x -= mod;
}
经此修改,可以拿到95分。
#include<bits/stdc++.h>
using namespace std;
const int h=200010;
inline int read() {
int s = 0, w = 1;
char ch = getchar();
while(ch < '0' || ch > '9') {
if(ch == '-') w= -1;
ch = getchar();
}
while(ch >= '0' && ch <= '9') {
s = s * 10 + ch - '0';
ch = getchar();
}
return s * w;
}
int mod=1e9+7;
inline void update(int &x, int y) {
x += y;
if(x >= mod) x -= mod;
} int n,m;
int a[h];
int b_sum[h];
int len;
int pre[2010][2010];
int suf[2010][2010];
int get_pos(int x){
return (x-1)/len+1;
}
int main(){
n=read(),m=read();
len=120;
for(int i=1;i<=n;i++)
a[i]=read(),update(b_sum[get_pos(i)],a[i]);
int op,x,y,z;
for(int i=1;i<=m;i++){
op=read(),x=read(),y=read();
if(op==1){
z=read();
if(x>=len)
for(int j=y;j<=n;j+=x)
update(a[j],z),update(b_sum[get_pos(j)],z);
else{
for(int j=y;j<=x;j++)
update(pre[x][j],z);
for(int j=1;j<=y;j++)
update(suf[x][j],z);
}
}
else{
int l=x,r=y,lb=get_pos(x),rb=get_pos(y);
int ans=0;
if(lb==rb)
for(int j=l;j<=r;j++)
update(ans,a[j]);
else{
for(int j=l;j<=lb*len;j++)
update(ans,a[j]);
for(int j=lb+1;j<=rb-1;j++)
update(ans,b_sum[j]);
for(int j=(rb-1)*len+1;j<=r;j++)
update(ans,a[j]); } for(int j=1;j<len;j++){
lb=(l-1)/j+1,rb=(r-1)/j+1;
if(lb==rb)
ans-=pre[j][(l-1)%j],ans%=mod,ans+=pre[j][(r-1)%j+1],ans%=mod;
else
ans=(ans+1ll*(rb-lb-1)*pre[j][j]+pre[j][(r-1)%j+1]+suf[j][(l-1)%j+1])%mod;
}
ans = ans % mod + mod;
ans = (ans >= mod) ? ans - mod : ans;
printf("%d\n",ans);
}
} return 0;
}
95分优化取模
技巧二:块长乱搞
这道题目需要使用分块。
而分块的时间复杂度往往随着块长而剧烈波动。
于是我们不断尝试新的块长。(为了节约评测机资源可以二分寻找)
得出最优块长在135左右,通过了这道题,耗时为5.93s。
#include<bits/stdc++.h>
using namespace std;
const int h=200010;
inline int read() {
int s = 0, w = 1;
char ch = getchar();
while(ch < '0' || ch > '9') {
if(ch == '-') w= -1;
ch = getchar();
}
while(ch >= '0' && ch <= '9') {
s = s * 10 + ch - '0';
ch = getchar();
}
return s * w;
}
int mod=1e9+7;
inline void update(int &x, int y) {
x += y;
if(x >= mod) x -= mod;
} int n,m;
int a[h];
int b_sum[h];
int len;
int pre[2010][2010];
int suf[2010][2010];
int get_pos(int x){
return (x-1)/len+1;
}
int main(){
n=read(),m=read();
len=135;
for(int i=1;i<=n;i++)
a[i]=read(),update(b_sum[get_pos(i)],a[i]);
int op,x,y,z;
for(int i=1;i<=m;i++){
op=read(),x=read(),y=read();
if(op==1){
z=read();
if(x>=len)
for(int j=y;j<=n;j+=x)
update(a[j],z),update(b_sum[get_pos(j)],z);
else{
for(int j=y;j<=x;j++)
update(pre[x][j],z);
for(int j=1;j<=y;j++)
update(suf[x][j],z);
}
}
else{
int l=x,r=y,lb=get_pos(x),rb=get_pos(y);
int ans=0;
if(lb==rb)
for(int j=l;j<=r;j++)
update(ans,a[j]);
else{
for(int j=l;j<=lb*len;j++)
update(ans,a[j]);
for(int j=lb+1;j<=rb-1;j++)
update(ans,b_sum[j]);
for(int j=(rb-1)*len+1;j<=r;j++)
update(ans,a[j]); } for(int j=1;j<len;j++){
lb=(l-1)/j+1,rb=(r-1)/j+1;
if(lb==rb)
ans-=pre[j][(l-1)%j],ans%=mod,ans+=pre[j][(r-1)%j+1],ans%=mod;
else
ans=(ans+1ll*(rb-lb-1)*pre[j][j]+pre[j][(r-1)%j+1]+suf[j][(l-1)%j+1])%mod;
}
ans = ans % mod + mod;
ans = (ans >= mod) ? ans - mod : ans;
printf("%d\n",ans);
}
} return 0;
}
100分块长优化
技巧三:按照题目内容实行特定优化
以下内容参考自原题目讨论版。
不同的题目有不同的步骤,这道题里这样一个步骤耗时巨大。
for(int j=1;j<len;j++){
lb=(l-1)/j+1,rb=(r-1)/j+1;
if(lb==rb)
ans-=pre[j][(l-1)%j],ans%=mod,ans+=pre[j][(r-1)%j+1],ans%=mod;
else
ans=(ans+1ll*(rb-lb-1)*pre[j][j]+pre[j][(r-1)%j+1]+suf[j][(l-1)%j+1])%mod;
}
这个地方运算很慢(因为一些原因套不了取模优化),而且当这个因数没有修改的时候,这些运算完全没有必要。
于是,如果这个因数没有进行修改,我们跳过后面的运算步骤。
for(int j=1;j<len;j++){
if(!pre[j][j])
continue;
lb=(l-1)/j+1,rb=(r-1)/j+1;
if(lb==rb)
ans-=pre[j][(l-1)%j],ans%=mod,ans+=pre[j][(r-1)%j+1],ans%=mod;
else
ans=(ans+1ll*(rb-lb-1)*pre[j][j]+pre[j][(r-1)%j+1]+suf[j][(l-1)%j+1])%mod;
}
以及在没有修改的情况下,弄一个前缀和,查询区间和直接调用。
耗时3.06s。
#include<bits/stdc++.h>
using namespace std;
const int h=200010;
inline int read() {
int s = 0, w = 1;
char ch = getchar();
while(ch < '0' || ch > '9') {
if(ch == '-') w= -1;
ch = getchar();
}
while(ch >= '0' && ch <= '9') {
s = s * 10 + ch - '0';
ch = getchar();
}
return s * w;
}
int mod=1e9+7;
inline void update(int &x, int y) {
x += y;
if(x >= mod) x -= mod;
} int n,m;
int a[h];
int b_sum[h];
int len;
int pre[2010][2010];
int suf[2010][2010];
int sum[h];
int get_pos(int x){
return (x-1)/len+1;
}
int main(){
n=read(),m=read();
len=150;
for(int i=1;i<=n;i++)
a[i]=read(),update(b_sum[get_pos(i)],a[i]),update(sum[i],a[i]+sum[i-1]);
int op,x,y,z;
bool fl=0;
for(int i=1;i<=m;i++){
op=read(),x=read(),y=read();
if(op==1){
z=read();
if(x>=len)
for(int j=y;j<=n;j+=x)
fl|=1,update(a[j],z),update(b_sum[get_pos(j)],z);
else{
for(int j=y;j<=x;j++)
update(pre[x][j],z);
for(int j=1;j<=y;j++)
update(suf[x][j],z);
}
}
else{ int l=x,r=y,lb=get_pos(x),rb=get_pos(y);
int ans=0;
if(!fl) ans+=sum[r]-sum[l-1];
else
if(lb==rb)
for(int j=l;j<=r;j++)
update(ans,a[j]);
else{
for(int j=l;j<=lb*len;j++)
update(ans,a[j]);
for(int j=lb+1;j<=rb-1;j++)
update(ans,b_sum[j]);
for(int j=(rb-1)*len+1;j<=r;j++)
update(ans,a[j]); } for(int j=1;j<len;j++){
if(!pre[j][j])
continue;
lb=(l-1)/j+1,rb=(r-1)/j+1;
if(lb==rb)
ans-=pre[j][(l-1)%j],ans%=mod,ans+=pre[j][(r-1)%j+1],ans%=mod;
else
ans=(ans+1ll*(rb-lb-1)*pre[j][j]+pre[j][(r-1)%j+1]+suf[j][(l-1)%j+1])%mod;
}
ans = ans % mod + mod;
ans = (ans >= mod) ? ans - mod : ans;
printf("%d\n",ans);
}
} return 0;
}
技巧四:节约空间
开数组也是要消耗很多时间的,我们进行优化。
int b_sum[1510];
int len;
int pre[161][161];
int suf[161][161];
时间优化至3.00s。
技巧五:奇技淫巧
到这一步,接下来的卡常技巧就没有学习的必要了。
比如把mod定义成成const可以快到2.52s。
将提交语言改为c++98可以快到2.4s。
然后是...然后自己看吧,没什么意义。
(主要由@Foofish大佬贡献)
//#pragma G++ target("avx")
//#pragma G++ optimize("Ofast")
#include<bits/stdc++.h>
using namespace std;
const int h=200010;
inline int read() {
register int s = 0, w = 1;
char ch = getchar();
while(ch < '0' || ch > '9') {
if(ch == '-') w= -1;
ch = getchar();
}
while(ch >= '0' && ch <= '9') {
s = s * 10 + ch - '0';
ch = getchar();
}
return s * w;
}
bool flag=0;
const int mod=1e9+7;
int n,m;
int a[h];
int b_sum[2010];
int len;
int pre[132][132];//pre[x][y]即modify(x,1)+modify(x,2)+...+modify(x,y)
int suf[132][132];
int sum[h];
int b_len;
int POS[201000];
#define get_pos(x) POS[x]
inline void write(int x) {
if(x >= 10) write(x / 10);
putchar(x % 10 + '0');
}
inline void update(int &x, int y) {
x += y;
if(x >= mod) x -= mod;
}
int main(){
//ios::sync_with_stdio(0);
n=read(),m=read();
len=130;
int Bnum = n / len;
for(int i = 1; i <= Bnum + 1; ++i)
for(int j = (i - 1) * len + 1; j <= i * len; ++j)
POS[j] = i;
for(int i=1;i<=n;i++)
a[i]=read(),update(b_sum[get_pos(i)], a[i]),update(sum[i], sum[i - 1] + a[i]);
int op,x,y,z;
for(int i=1;i<=m;i++){
op=read(),x=read(),y=read();
if(op==1){
z=read();
if(x>=len)
for(int j=y;j<=n;j+=x)
flag|=1,update(a[j], z),update(b_sum[get_pos(j)], z);
else{
for(int j=y;j<=x;j++)
update(pre[x][j], z);
for(int j=1;j<=y;j++)
update(suf[x][j], z);
}
}
else{
int l=x,r=y,lb=get_pos(x),rb=get_pos(y);
int ans=0;
if(!flag)
ans+=sum[r]-sum[l-1];
else
if(lb==rb)
for(int j=l;j<=r;j++)
update(ans, a[j]);
else{
for(int j=l;j<=lb*len;j++)
update(ans, a[j]);
for(int j=lb+1;j<=rb-1;j++)
update(ans, b_sum[j]);
for(int j=(rb-1)*len+1;j<=r;j++)
update(ans, a[j]); } for(int j=1;j<len;j++){
if(!pre[j][j])
continue;
lb=(l-1)/j+1,rb=(r-1)/j+1;
if(lb==rb) {
ans-=pre[j][(l-1)%j],ans%=mod,ans+=pre[j][(r-1)%j+1],ans%=mod;
// ans = (ans - pre[j][(l - 1) % j] + pre[j][(r - 1) % j + 1] + mod) % mod;
}
else {
ans=(ans+1ll*(rb-lb-1)*pre[j][j]+pre[j][(r-1)%j+1]+suf[j][(l-1)%j+1])%mod;
}
}
ans = ans % mod + mod;
ans = (ans >= mod) ? ans - mod : ans;
printf("%d",ans); puts("");
}
} return 0;
}
耗时2.38s
#define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++)
char buf[1<<21],*p1=buf,*p2=buf;
template<typename T>inline void read(T& t){
int c=getchar(),f=1;t=0;
while(!isdigit(c)){if(c=='-')f=-1;c=getchar();}
while(isdigit(c))t=t*10+c-48,c=getchar();t*=f;
}
template<typename T,typename ...Args>inline void read(T& t,Args&... args){
read(t),read(args...);
}
*某位大佬提供的快读,安装之后时间达到了2.23s。
目前能达到的最短时间:c++ 14 无O2优化 2.21s
#pragma G++ target("avx")
#pragma G++ optimize("Ofast")
#include<bits/stdc++.h>
using namespace std;
const int h=200010;
#define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++)
char buf[1<<21],*p1=buf,*p2=buf;
template<typename T>inline void read(T& t){
int c=getchar(),f=1;t=0;
while(!isdigit(c)){if(c=='-')f=-1;c=getchar();}
while(isdigit(c))t=t*10+c-48,c=getchar();t*=f;
}
template<typename T,typename ...Args>inline void read(T& t,Args&... args){
read(t),read(args...);
}
bool flag=0;
const int mod=1e9+7;
int n,m;
int a[h];
int b_sum[2010];
int len;
int pre[132][132];//pre[x][y]即modify(x,1)+modify(x,2)+...+modify(x,y)
int suf[132][132];
int sum[h];
int b_len;
int POS[201000];
#define get_pos(x) POS[x]
inline void write(int x) {
if(x >= 10) write(x / 10);
putchar(x % 10 + '0');
}
inline void update(int &x, int y) {
x += y;
if(x >= mod) x -= mod;
}
int main(){
//ios::sync_with_stdio(0);
read(n,m);
len=130;
int Bnum = n / len;
for(int i = 1; i <= Bnum + 1; ++i)
for(int j = (i - 1)* len + 1,ran= i * len; j <=ran ; ++j)
POS[j] = i;
for(int i=1;i<=n;i++)
read(a[i]),update(b_sum[get_pos(i)], a[i]),update(sum[i], sum[i - 1] + a[i]);
int op,x,y,z;
for(int i=1;i<=m;i++){
read(op,x,y);
if(op==1){
read(z);
if(x>=len)
for(int j=y;j<=n;j+=x)
flag|=1,update(a[j], z),update(b_sum[get_pos(j)], z);
else{
for(int j=y;j<=x;j++)
update(pre[x][j], z);
for(int j=1;j<=y;j++)
update(suf[x][j], z);
}
}
else{
int l=x,r=y,lb=get_pos(x),rb=get_pos(y);
int ans=0;
if(!flag)
ans+=sum[r]-sum[l-1];
else
if(lb==rb)
for(int j=l;j<=r;j++)
update(ans, a[j]);
else{
for(int j=l,ran=lb*len;j<=ran;j++)
update(ans, a[j]);
for(int j=lb+1;j<=rb-1;j++)
update(ans, b_sum[j]);
for(int j=(rb-1)*len+1;j<=r;j++)
update(ans, a[j]); } for(int j=1;j<len;j++){
if(!pre[j][j])
continue;
lb=(l-1)/j+1,rb=(r-1)/j+1;
if(lb==rb) {
ans-=pre[j][(l-1)%j],ans%=mod,ans+=pre[j][(r-1)%j+1],ans%=mod;
// ans = (ans - pre[j][(l - 1) % j] + pre[j][(r - 1) % j + 1] + mod) % mod;
}
else {
ans=(ans+1ll*(rb-lb-1)*pre[j][j]+pre[j][(r-1)%j+1]+suf[j][(l-1)%j+1])%mod;
}
}
ans = ans % mod + mod;
ans = (ans >= mod) ? ans - mod : ans;
printf("%d",ans); puts("");
}
} return 0;
}
Code
从 洛谷P5309 Ynoi2011 初始化 看卡常的更多相关文章
- 洛谷P5309 Ynoi 2011 初始化 题解
题面. 我也想过根号分治,但是题目刷得少,数组不敢开,所以还是看题解做的. 这道题目要用到根号分治的思想,可以看看这道题目和我的题解. 题目要求处理一个数组a,支持如下操作. 对一个整数x,对数组长度 ...
- 洛谷 P1186 【玛丽卡】
这道题题目真的想吐槽一下...是在机房同学的解释下才看懂的.就是让你求在可以删一条边的情况下,并且删后保证可以到达终点时,求删了后的最大的最短路径. 70分暴力思路: 枚举删边,然后跑一下最短路即可, ...
- 洛谷 P1641 [SCOI2010]生成字符串
洛谷 这题一看就是卡塔兰数. 因为\(cnt[1] \leq cnt[0]\),很显然的卡塔兰嘛! 平时我们推导卡塔兰是用一个边长为n的正方形推的, 相当于从(0,0)点走到(n,n)点,向上走的步数 ...
- 不失一般性和快捷性地判定决策单调(洛谷P1912 [NOI2009]诗人小G)(动态规划,决策单调性,单调队列)
洛谷题目传送门 闲话 看完洛谷larryzhong巨佬的题解,蒟蒻一脸懵逼 如果哪年NOI(放心我这样的蒟蒻是去不了的)又来个决策单调性优化DP,那蒟蒻是不是会看都看不出来直接爆\(0\)?! 还是要 ...
- 【洛谷3674】小清新人渣的本愿(莫队,bitset)
[洛谷3674]小清新人渣的本愿(莫队,bitset) 题面 洛谷,自己去看去,太长了 题解 很显然的莫队. 但是怎么查询那几个询问. 对于询问乘积,显然可以暴力枚举因数(反正加起来也是\(O(n\s ...
- BZOJ1060或洛谷1131 [ZJOI2007]时态同步
BZOJ原题链接 洛谷原题链接 看上去就觉得是一道树形\(\mathtt{DP}\),不过到头来我发现我写了一个贪心.. 显然对越靠近根(记为\(r\))的边进行加权贡献越大,且同步的时间显然是从根到 ...
- 【流水调度问题】【邻项交换对比】【Johnson法则】洛谷P1080国王游戏/P1248加工生产调度/P2123皇后游戏/P1541爬山
前提说明,因为我比较菜,关于理论性的证明大部分是搬来其他大佬的,相应地方有注明. 我自己写的部分换颜色来便于区分. 邻项交换对比是求一定条件下的最优排序的思想(个人理解).这部分最近做了一些题,就一起 ...
- 洛谷 P6295 - 有标号 DAG 计数(生成函数+容斥+NTT)
洛谷题面传送门 看到图计数的题就条件反射地认为是不可做题并点开了题解--实际上这题以我现在的水平还是有可能能独立解决的( 首先连通这个条件有点棘手,我们尝试把它去掉.考虑这题的套路,我们设 \(f_n ...
- 快速排序--洛谷卡TLE后最终我还是选择了三向切割
写在前边 这篇文章呢,我们接着聊一下排序算法,我们之前已经谈到了简单插入排序 和ta的优化版希尔排序,这节我们要接触一个更"高级"的算法了--快速排序. 在做洛谷的时候,遇到了一道 ...
随机推荐
- Spring(二)-生命周期 + 自动装配(xml) +自动装配(注解)
1.生命周期 **Spring容器的 bean **的生命周期: 1.1 默认生命周期 1.1.1 生命周期 调用构造方法,创建实例对象: set方法,给实例对象赋值: init 初始化方法 初始化对 ...
- Django-Import-Export插件控制数据导入流程
前言 之前写过两篇跟这个插件有关的文章,可以回顾一下: Django数据导入导出神器django-import-export使用 Django-Import-Export插件关于外键的处理 最近有个朋 ...
- 并发编程二、CPU多级缓存架构与MESI协议的诞生
前言: 文章内容:线程与进程.线程生命周期.线程中断.线程常见问题总结 本文章内容来源于笔者学习笔记,内容可能与相关书籍内容重合 偏向于知识核心总结,非零基础学习文章,可用于知识的体系建立,核心内容 ...
- Windows如何创存储虚拟机并制作存储虚拟化LUN的映射
创建虚拟机 只能设置为8G,不能多也不能少 选择仅主机模式 选择使用现有磁盘 浏览选择自己的vmdk文件 选择保存现有格式 点击完成 点击编辑虚拟机设置 添加一个40G的硬盘 修改为40G并选择存储为 ...
- 运维利器-ClusterShell
前言 和ansible类似,但是更加高效 安装 yum install -y clustershell clush命令: clush -a 全部 等于 clush -g all clush -g 指定 ...
- c语言字符串比较与bool型
c++字符串string,定义的变量,能够通过比较符号,直接进行比较. 而c语言则不能通过char数组定义的变量,来直接比较.应用下面的方法: #include <string.h> in ...
- Kubernetes 监控:Prometheus Operator
安装 前面的章节中我们学习了用自定义的方式来对 Kubernetes 集群进行监控,基本上也能够完成监控报警的需求了.但实际上对上 Kubernetes 来说,还有更简单方式来监控报警,那就是 Pro ...
- Kubernetes 监控--Grafana
前面我们使用 Prometheus 采集了 Kubernetes 集群中的一些监控数据指标,我们也尝试使用 promQL 语句查询出了一些数据,并且在 Prometheus 的 Dashboard 中 ...
- 第二章:视图层 - 5:HttpRequest对象
每当一个用户请求发送过来,Django将HTTP数据包中的相关内容,打包成为一个HttpRequest对象,并传递给每个视图函数作为第一位置参数,也就是request,供我们调用. HttpReque ...
- 在 K8S 上部署以 mysql 数据库作为后端存储的单机版 nacos
Nacos 被用于: 服务发现 微服务配置信息管理 部署 nacos 时,需要用到如下两个镜像,这两个镜像均来自于 nacos 官方发布到 docker hub 的镜像, nacos/nacos-se ...