link
正在肝的一道黑題……
首先處理那個奇怪的詢問操作。我們可以對於那個奇怪的東西進行暴力推式:
\[ans(l,r)=\sum\limits_^r\sum\limits_^\sum\limits_^
\]用字首和搞一下就是
\[\sum\limits_^r\sum\limits_^-sum_}
\]分離一下
\[\sum\limits_^r^m-\sum\limits_^sum_i)}
\]emmmm,發現它似乎仍然具有字首和的性質,考慮搞個字首和的字首和(\(su\)):
\[\sum\limits_^r-su_-su_}
\]展開:
\[su_m\times(r-l+1)-\sum\limits_^su_i-\sum\limits_^
\]於是那個奇怪的詢問就變成了單純的對於su陣列的區間和查詢。
按照思考此類問題的慣常方法,考慮假如我們給\([l,r]\)中的數都加了乙個\(a\),會發生些什麼?
\[\delta sum_i=\begin0&i\in[1,l-1]\\d\times(i-l+1)&i\in[l,r]\\d\times(r-l+1)&i\in[r+1,m]\end
\]同樣可以得出,假如\(s(i)=i\times(i+1)/2\):
\[\delta su_i=\begin0&i\in[1,l-1]\\d\times s(i-l+1)&i\in[l,r]\\d\times s(r-l+1)+d\times(r-l+1)\times(i-r)&i\in[r+1,m]\end
\]開心地發現\(i\in[1,l-1]\)部分不用管,所以對於剩下的兩個部分進行處理。首先是\(i\in[r+1,m]\)部分,令\(len=r-l+1\)。
\[\delta su_i=d\times s(len)+d\times len\times(i-r)\\=d\times s(len)-d\times len\times r+d\times len\times r
\]發現前半部分是個定值,相當於是普通區間加;後半部分相當於是區間加 \(d\times len\) 倍的下標。如何實現後面再說。再看一下對於 \(i\in[r+1,m]\) 部分怎麼處理。
\[\frac=(i-l+1)^2+(i-l+1)\\=i^2+(l-1)^2+2i(1-l)+i+(1-l)\\=i^2+(3-2l)i+(l-1)(l-2)
\]所以
\[2\delta su_i=di^2+(3d-2ld)i+(l-1)(l-2)d
\]發現\((l-1)(l-2)\)是固定的,相當於是區間加。於是我們就需要一種資料結構可以完成區間加下標平方和區間加下標。這應該是可以做到的。區間加下標好辦,那區間加下標平方曾麼搞?有平方和公式啊。
\[\sum\limits_^=\frac
\]最後我們發現,上面推出來的柿子為真正增量的兩倍。取模意義下除法灰常麻煩,於是我的做法是線段樹中維護的是增量的兩倍,詢問時乘上2的逆元就可以了。
最後說明一點,由於這道題對取模的要求苛刻得有些喪心病狂,一定要認真取模。然後本蒟蒻**能力極差,碼風自然氫氣,見諒。
#include//#define zczc
#define int long long
const int n=200010;
const int yy=500000004;
const int mod=1000000007;
inline void read(int &wh)
while(w<='9'&&w>='0')
wh*=f;return;
}int m,n,a[n];
#define lc (wh<<1)
#define rc (wh<<1|1)
#define mid (t[wh].l+t[wh].r>>1)
//直接區間加的那棵樹
namespace tat[n<<2];
inline void pushup(int wh)
inline void pushnow(int wh,int val)
inline void pushdown(int wh)
return;
} inline void build(int wh,int l,int r)
build(lc,l,mid);
build(rc,mid+1,r);
pushup(wh);
return;
} inline void change(int wh,int wl,int wr,int val)
pushdown(wh);
if(wl<=mid)change(lc,wl,wr,val);
if(wr>mid)change(rc,wl,wr,val);
pushup(wh);
return;
} inline int work(int wh,int wl,int wr)
pushdown(wh);
int an=0;
if(wl<=mid)an+=work(lc,wl,wr);
if(wr>mid)an+=work(rc,wl,wr);
return an%mod;
} inline int ask(int wh,int pl)
}//區間加下標的那棵樹
namespace tbt[n<<2];
inline void pushup(int wh)
inline int s(int l,int r)
inline void pushnow(int wh,int val)
inline void pushdown(int wh)
return;
} inline void build(int wh,int l,int r)
inline void change(int wh,int wl,int wr,int val)
pushdown(wh);
if(wl<=mid)change(lc,wl,wr,val);
if(wr>mid)change(rc,wl,wr,val);
pushup(wh);
return;
} inline int work(int wh,int wl,int wr)
pushdown(wh);
int an=0;
if(wl<=mid)an+=work(lc,wl,wr);
if(wr>mid)an+=work(rc,wl,wr);
return an%mod;
} inline int ask(int wh,int pl)
}//區間加下標平方的那棵樹
namespace tct[n<<2];
inline void pushup(int wh)
inline int s(int wh)
inline void pushnow(int wh,int val)
inline void pushdown(int wh)
return;
} inline void build(int wh,int l,int r)
inline void change(int wh,int wl,int wr,int val)
pushdown(wh);
if(wl<=mid)change(lc,wl,wr,val);
if(wr>mid)change(rc,wl,wr,val);
pushup(wh);
return;
} inline int work(int wh,int wl,int wr)
pushdown(wh);
int an=0;
if(wl<=mid)an+=work(lc,wl,wr);
if(wr>mid)an+=work(rc,wl,wr);
return an%mod;
} inline int ask(int wh,int pl)
} #undef lc
#undef rc
#undef mid
inline int solve(int l,int r)
else
}signed main()
for(int i=1;i<=m;i++)a[i]+=a[i-1],a[i]%=mod;
//for(int i=1;i<=m;i++)printf("%d ",a[i]);
ta::build(1,0,m);
tb::build(1,0,m);
tc::build(1,0,m);
int op,l,r,val;
while(n--)
if(op==2)
else
ta::change(1,l,r,(l-1)*(l-2)%mod*val%mod*yy%mod);
tb::change(1,l,r,val*(3-2*l)%mod);
tc::change(1,l,r,val);
} }return 0;
}
BJOI2018 鏈上二次求和
bjoi2018 鏈上二次求和 我說今天上午寫部落格吧。怕自己寫一上午,就決定先寫道題。然後我就調了一上午線段樹。花了2h找到lazy標記沒有清空。我tm清空了有沒有標記沒清空標記本身。又花25min找到某個乘法爆int了。int真的淡疼,要不是longlong自帶巨無霸常數,這輩子都不想用int。...
BJOI2018鏈上二次求和 線段樹
l,r 的限制可以拆成 1,l 1 1,r 然後考慮推式子。設s is i si 為a ia i ai 的字首和。a ns i 1x j ins j sj i sum sum s s ans i 1x j i n sj sj i 設t it i ti 為s is i si 的字首和。a ns i 1...
題解 QLU第二次測試賽 求和
要滿足體面所說三元組,需x y z y x z y color x color z 首先我們考慮暴力的解法 for int y 1 y n y 1.y x z y 2 y x z 即 x z 肯定為偶數,即x與z需同奇或同偶 2.y z x 2 即便x與z再大只要x,z n,y include in...