2020.3
3.19
學習ST表,處理區間求最值。
對於f[i][j]爲從i開始的2^j個數中的最值,那麼終點爲i + 2^j -1,區間長度爲2^j 。
對於一個區間[l,r],首先求出區間長度k=log2(r-l+1) 。
那麼區間最值由[l,l+2^k-1] 及 [ r-2^k+1,r] 保證一定可以覆蓋查詢的區間。
//f[i][0]爲自己。
//鬆弛區間
for(int j=1;j<=21;j++)
for(int i=1;i+(1<<j)-1<=n;i++)
f[i][j]=max(f[i][j-1],f[i+(1<<(j-1))][j-1]);
//詢問區間
ll l=read(),r=read();
ll k=log2(r-l+1);
cout<<max(f[l][k],f[r+1-(1<<k)][k])<<"\n";
3.20
學習乘法逆元,假設有p=ki+r:
取模意義下: ki + r mod p == 0
乘上i^(-1) 以及r^(-1) :i^(-1)= - k*r^(-1)
代換: i^(-1) = ( - (p/i) * (p%i)^(-1) ) %p
有: i^(-1) = (p-(p/i) *(p%i)^(-1) )%p
其中inv[1]=1
inv[1]=1;
for(int i=2;i<=n;i++)
inv[i]=((p-p/i)*inv[p%i])%p;
3.21
學習盧卡斯定理,處理組合數取模的問題。
Lucas(n,m,p)=C(n%p,m%p,p)×Lucas( n/p,m/p,p)%p
其中Lucas(n,0,p)=1
ll Inv(ll x,ll p){return qp(x,p-2,p);}
ll Cal(ll n,ll m,ll p){
if(m>n) return 0;
ll ans=1;
for(int i=1;i<=m;++i)ans=ans*Inv(i,p)%p*(n-i+1)%p;
return ans%p;
}
ll Lucas(ll n,ll m,ll p){
if(!m) return 1;
return Cal(n%p,m%p,p)*Lucas(n/p,m/p,p)%p;
}
學習單調隊列,處理滑動窗口最值問題。
當q[head]+m<=i說明隊首元素已經跟不上滑動窗口。
當a[ q[tail] ]<a[i]說明隊尾元素比當前元素小,失去優先級。
for(int i=1;i<=n;i++){
while(head<=tail&&q[head]+m<=i) ++head;
while(head<=tail&&a[q[tail]]<a[i]) --tail;
q[++tail]=i;
}
3.22
簡單學了一下矩陣快速冪,主要的收穫還是重載運算符。
int n;
struct node{
ll x[manx][manx];
node(){ memset(x,0,sizeof(x));}
inline void init(){ for(int i=1;i<=n;i++) x[i][i]=1; }
};
node operator *(const node &a, const node &b){
node res;
for(int k=1;k<=n;k++)
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
res.x[i][j]=(res.x[i][j]+a.x[i][k]*b.x[k][j])%mod;
return res;
}
3.23
學習tarjan關於縮點的問題,覺得還是新開兩個數組來存邊再進行縮點比較好。
for(int i=1;i<=m;i++){
u=ssc[x[i]],v=ssc[y[i]];
if(u!=v) add(u,v);
}
3.24
聽學校的大佬指導,重新學習線段樹,今天先手擼個區間操作的線段樹。
ll s[manx*4],a[manx],f[manx*4],L[manx*4],R[manx*4],ans=0;
inline void build(ll k,ll l,ll r){
L[k]=l,R[k]=r;
if(L[k]==R[k]){
s[k]=a[l];
return ;
}
ll mid=(L[k]+R[k])>>1;
build(k<<1,l,mid);
build(k<<1|1,mid+1,r);
s[k]=s[k<<1]+s[k<<1|1];
}
inline void down(ll k){
ll l=k<<1,r= k<<1|1;
f[l]+=f[k]; f[r]+=f[k];
s[l]+=(R[l]-L[l]+1)*f[k];
s[r]+=(R[r]-L[r]+1)*f[k];
f[k]=0;
}
inline void add(ll k,ll l,ll r,ll x){
if(L[k]>=l&&R[k]<=r){
s[k]+=(R[k]-L[k]+1)*x;
f[k]+=x;
return ;
}
if(f[k]) down(k);
ll mid=(L[k]+R[k])>>1;
if(l<=mid) add(k<<1,l,r,x);
if(r>mid) add(k<<1|1,l,r,x);
s[k]=s[k<<1]+s[k<<1|1];
}
inline void finds(ll k,ll l,ll r){
if(L[k]>=l&&R[k]<=r){
ans+=s[k];
return ;
}
if(f[k]) down(k);
ll mid=(L[k]+R[k])>>1;
if(l<=mid) finds(k<<1,l,r);
if(r>mid) finds(k<<1|1,l,r);
}
3.25
滿課,只能記錄下小知識點。
priority_queue<ll>q; // 大根堆
priority_queue<ll,vector<ll>,greater<ll> >q; //小根堆
sort(v.begin(),v.end()); //從小到大
sort(v.begin(),v.end(),greater<ll>()); //從大到小
26-28 幾天跟朋友出去放鬆了幾天,接下來繼續。
3.29
卡特蘭數:dp[i] = dp[i-1] * (4*i-2) / (i+1)
特殊的 : dp[0]=0 , dp[1]=1
f[0]=0,f[1]=1;
for(int i=2;i<=n;i++)
f[i]=f[i-1]*(4*i-2)/(i+1);
3.30
學習樹狀數組求逆序數。
就樹狀數組而言其實就類似於一個區間和數據結構,那麼優化的就是求逆序數時求前面的 i - 1個點對 i 點的貢獻和,這個過程很簡單,遍歷時把 a[i] 放到樹上,那麼在求樹中小於等於 a[i] 的元素個數時,不難利用 i - gets(a[i]) 得到樹中比 a[i] 大的元素個數 。
ll gets(ll x){
ll cnt=0;
while(x){
cnt+=s[x];
x-=lowbit(x);
}
return cnt;
}
void add(ll x){
while(x<=n){
s[x]++;
x+=lowbit(x);
}
}
ll ans=0;
for(int i=1;i<=n;i++){
add(a[i]);
ans+=i-gets(a[i]);
}