三月之从零开始的ACM

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:
取模意义下: k
i + 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] ]

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]);
}

你可能感兴趣的:(acm)