Luogu3373 模板 線段樹 2

2022-05-17 14:47:03 字數 4364 閱讀 7442

不寫線段樹,就是要分塊

同樣需要打標記

在任何時候,\(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。如果兩個標記要疊加,標記疊加前區間上的和...