不寫線段樹,就是要分塊!
同樣需要打標記
在任何時候,\(a_i=mul_ \times a_i+add_\)(\(bel_i\)表示\(i\)屬於哪一塊,\(a_i\)表示第\(i\)個位置的真實值)
但是由於標記都是整塊整塊打上去的,無法單點修改,本來可能還可以利用逆元強行修改,問題是\(p=571373\)不是質數,那麼如何處理零散的數呢
很簡單,因為零散的數最多分布再兩塊中,我們直接把那兩塊標記統統下傳,這樣就不需要考慮標記的事了
結果,\(tle(70pts)\)。。。
\(tle code:\)
#pragma gcc optimize(2)
#pragma gcc optimize(3)
#pragma gcc optimize("-o2")
#pragma gcc optimize("-o3")
#pragma gcc optimize("inline")
#include#include#include#include#define ll long long
#define n 100005
using namespace std;
int n,m,opt,x,y,p,l[n],r[n],bel[n];
int c,a[n],add[n],mul[n],s[n];
void push_down(int x)
int main()
for (int i=1;i<=bel[n];i++)
add[i]=0,mul[i]=1;
while (m --> 0)
push_down(rx),push_down(ry);
for (int i=x;i<=r[rx];i++)
s[rx]=((ll)s[rx]+(ll)a[i]*(c-1)%p),a[i]=(ll)a[i]*c%p;
for (int i=rx+1;i<=ry-1;i++)
add[i]=(ll)add[i]*c%p,mul[i]=(ll)mul[i]*c%p,s[i]=(ll)s[i]*c%p;
for (int i=l[ry];i<=y;i++)
s[ry]=((ll)s[ry]+(ll)a[i]*(c-1)%p),a[i]=(ll)a[i]*c%p;
} else
if (opt==2)
push_down(rx),push_down(ry);
for (int i=x;i<=r[rx];i++)
a[i]=(a[i]+c)%p;
s[rx]=((ll)s[rx]+(ll)c*(r[rx]-x+1))%p;
for (int i=rx+1;i<=ry-1;i++)
add[i]=(add[i]+c)%p,s[i]=((ll)s[i]+(ll)c*(r[i]-l[i]+1))%p;
for (int i=l[ry];i<=y;i++)
a[i]=(a[i]+c)%p;
s[ry]=((ll)s[ry]+(ll)c*(y-l[ry]+1))%p;
} else
for (int i=x;i<=r[rx];i++)
ans=((ll)ans+(ll)mul[rx]*a[i]+add[rx])%p;
for (int i=rx+1;i<=ry-1;i++)
ans=(ans+s[i])%p;
for (int i=l[ry];i<=y;i++)
ans=((ll)ans+(ll)mul[ry]*a[i]+add[ry])%p;
ans=(ans%p+p)%p;
printf("%d\n",ans);}}
return 0;
}
怎麼辦,先尋找可以減小計算量的地方進行優化,發現依舊\(tle\)
終極操作:迴圈展開!
\(ac!\),總用時\(2.54s\),最大點\(950ms\)
於是**不堪入目
\(code:\)
#pragma gcc optimize(2)
#pragma gcc optimize(3)
#pragma gcc optimize("-o2")
#pragma gcc optimize("-o3")
#pragma gcc optimize("inline")
#include#include#include#include#define ll long long
#define n 100005
using namespace std;
int n,m,opt,x,y,p,l[n],r[n],len[n],bel[n];
int lx,wx,qx,qy,g;
int c,a[n],add[n],mul[n],s[n];
int read()
return s;
}void write(int x)
void push_down(int x)
int lx=l[x]/10*10+10;
int wx=r[x]/10*10;
int g=(wx-lx)/10;
for (int i=l[x];i<=lx;i++)
a[i]=((ll)a[i]*mul[x]+add[x])%p;
for (int i=wx+1;i<=r[x];i++)
a[i]=((ll)a[i]*mul[x]+add[x])%p;
for (int i=1;i<=g;i++)
add[x]=0,mul[x]=1;
}int main()
for (int i=1;i<=bel[n];i++)
add[i]=0,mul[i]=1,len[i]=r[i]-l[i]+1;
while (m --> 0)
push_down(rx);
qx=x,qy=y;
if (qx/10==qy/10)
lx=qx/10*10+10;
wx=qy/10*10;
g=(wx-lx)/10;
for (int i=qx;i<=lx;i++)
s[rx]=((ll)s[rx]+(ll)a[i]*(c-1)%p),a[i]=(ll)a[i]*c%p;
for (int i=wx+1;i<=qy;i++)
s[rx]=((ll)s[rx]+(ll)a[i]*(c-1)%p),a[i]=(ll)a[i]*c%p;
for (int i=1;i<=g;i++)
continue;
}if (x!=l[rx])
else
}rx++;
}if (y!=r[ry])
else
}ry--;
}if (rx>ry)
continue;
qx=rx,qy=ry;
if (qx/10==qy/10)
else
}} else
if (opt==2)
push_down(rx);
qx=x,qy=y;
if (qx/10==qy/10)
else
}s[rx]=((ll)s[rx]+(ll)c*(y-x+1))%p;
continue;
}if (x!=l[rx])
else
}s[rx]=((ll)s[rx]+(ll)c*(r[rx]-x+1))%p;
rx++;
}if (y!=r[ry])
else
}s[ry]=((ll)s[ry]+(ll)c*(y-l[ry]+1))%p;
ry--;
}if (rx>ry)
continue;
qx=rx,qy=ry;
if (qx/10==qy/10)
else
}} else
qx=x,qy=y;
ans=(ans+(ll)add[rx]*(qy-qx+1)%p);
if (qx/10==qy/10)
else
}ans=(ans%p+p)%p;
write(ans),putchar('\n');
continue;
}if (x!=l[rx])
else
}rx++;
}if (y!=r[ry])
else
}ry--;
}if (rx>ry)
qx=rx,qy=ry;
if (qx/10==qy/10)
else
}ans=(ans%p+p)%p;
write(ans),putchar('\n');}}
return 0;
}
luogu3373 模板 線段樹 2
題面 已知乙個數列,你需要進行下面三種操作 1.將某區間每乙個數乘上x 2.將某區間每乙個數加上x 3.求出某區間每乙個數的和 題解區間修改 區間查詢。維護兩個lazytag include include using namespace std const int maxn 100010 type...
luogu 3373 模板 線段樹2
如題,已知乙個數列,你需要進行下面兩種操作 1.將某區間每乙個數加上x 2.將某區間每乙個數乘上x 3.求出某區間每乙個數的和 輸入格式 第一行包含三個整數n m p,分別表示該數列數字的個數 操作的總個數和模數。第二行包含n個用空格分隔的整數,其中第i個數字表示數列第i項的初始值。接下來m行每行包...
luogu3373 模板 線段樹2
題目大意 已知乙個數列,你需要進行下面三種操作 1.將某區間每乙個數乘上x 2.將某區間每乙個數加上x 3.求出某區間每乙個數的和 本線段樹的標記是個二元組 add和mul,其代表將乙個線段中的每乙個點乘以mul再加add。設區間長度為x,原來區間和為sum。如果兩個標記要疊加,標記疊加前區間上的和...