LuoguP4433[COCI2009-2010#1] ALADIN(含类欧几里得算法推导)

Posted neosknight

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了LuoguP4433[COCI2009-2010#1] ALADIN(含类欧几里得算法推导)相关的知识,希望对你有一定的参考价值。

题目链接

题意简述

区间赋值模意义下等差数列,询问区间和
(Nleq 10^9,Qleq 10^5)

Sol

每次操作就是把操作区间([L,R])中的数赋值成:
[(X-L+1)*A mod B]
考虑用线段树维护。
我们只需要能快速知道一段区间([l,r])被覆盖后的和就行了,因为覆盖的标记易于下传:
[sum_{i=l}^{r} (i-L+1)*A mod B]
根据基础的数学知识,mod显然不好算,把它拆开:
[sum_{i=l}^r (i-L+1)*A-igglfloor frac{(i-L+1)*A}{B}igg floor*B]
前面那一坨直接算就行了,关键是后面这一坨。我们可以把后面的式子写简洁一些:
[B*sum_{i=q}^{p} igglfloor frac{A*i}{B}igg floor]

其中(p=l-L+1,q=r-l+1),(B)乘在外面不用管,而(p)(q)的求和可以拆分为(0sim q) 的和减去(0sim p-1)的和,这样我们只需要会算以下式子:
[sum_{i=0}^n igglfloor frac{A*i}{B}igg floor]

这玩意就可以用类欧几里得算法求,具体过程如下。


第一种方法是用几何的思想直观的来理解,但是似乎不是很好推出明确的式子,所以这里主要用代数推导。
对于式子:
[sum_{i=0}^n igglfloor frac{A*i+B}{C}igg floor]
其几何意义就是直线(y=dfrac{Ax+B}{C}) 下方和x轴与y轴与直线(x=n)围成的图形中包含的纵坐标不为0的整点个数,这个画个图就很好理解:
[令 F(n,A,B,C)=sum_{i=0}^n igglfloor frac{A*i+B}{C}igg floor]
如果 (A,B)不小于 (C),那么可以把整除的部分提出来直接计算,这个很简单,所以只讨论(A,B)都小于(C)的情况。

(m=iglfloor frac{A*n+B}{C}ig floor),通过几何意义转换(这里方便推式子j从0到m-1):
[F(n,A,B,C)=sum_{i=0}^nsum_{j=0}^{m-1} igg[(A*i+B) geq C*(j+1)igg]]
调换求和顺序
[F(n,A,B,C)=sum_{i=0}^{m-1}sum_{j=0}^nigg [(A*j+B)geq C*(i+1)igg]]

移项变形,减1去掉不等式的等号:
[F(n,A,B,C)=sum_{i=0}^{m-1}sum_{j=0}^n igg[(C*i+C-B-1) < A*jigg]]
接下来容斥一下,用总数减去不合法的情况:
[F(n,A,B,C)=sum_{i=0}^{m-1}(n+1-sum_{j=0}^n igg[(C*i+C-B-1) geq A*jigg])]

这一下里面的不就和原来我们的(F(x))的形式差不多了嘛。
考虑到我们之前(j)是从(0sim m-1)而当(j=0)的时候,左边必大于0,而右边为0,贡献一定存在,故可以把原式改写,先把1的贡献减在外面:
[F(n,A,B,C)=sum_{i=0}^{m-1}(n-sum_{j=0}^{n-1} igg[(C*i+C-B-1) geq A*(j+1)igg])]
这样不就提出(n)来:
[F(n,A,B,C)=n*m-sum_{i=0}^{m-1}sum_{j=0}^{n-1} igg[(C*i+C-B-1) geq A*(j+1)igg]]
后面的式子很先然可以根据(F(x))的定义改写:
[F(n,A,B,C)=n*m-F(m-1,C,C-B-1,A)]

这样递归下去不停计算,显然的是当最后(A)变成(0)的时候贡献为(0)
只用观察(A)(C),发现他们调换了位置,这样必然使得下一次的(C leq A),那么可以先把这部分提出来计算,实际上就是:((A,C) ightarrow (C\%A,A)),这不是就是和(gcd)长的一样吗,所以复杂度也是(O(logn))

其实类欧主要是一种把整除转化为求整点的思想

直接写类欧的代码差不多长这样:

ll likegcd(ll n,ll a,ll b,ll c){
    if(c<=a||c<=b) return (a/c*n%mod*(n+1)%mod*inv2%mod+b/c*(n+1)%mod+likegcd(n,a%c,b%c,c))%mod;
    if(!a||!n) return 0;
    ll m=(a*n+b)/c;
    return (n*m%mod-likegcd(m-1,c,c-b-1,a)+mod)%mod;
}

所以这道题也差不多做完了
但是有两个要注意的地方:
1.直接乘可能会爆(long long),所以(\_\_int128)大法好
2.本题卡空间所以要对操作区间和询问区间离散化,注意离散化的是(l-1和r)

代码:

#include<bits/stdc++.h>
using namespace std;
#define Set(a,b) memset(a,b,sizeof(a))
template<class T>inline void init(T&x){
    x=0;char ch=getchar();bool t=0;
    for(;ch<'0'||ch>'9';ch=getchar()) if(ch=='-') t=1;
    for(;ch>='0'&&ch<='9';ch=getchar()) x=(x<<1)+(x<<3)+(ch-48);
    if(t) x=-x;return;
}
int n,q;
//#define ll __int128
typedef long long ll;
const int MAXN=2e6+1;
const int M=5e4+1;
ll sum[MAXN];
int L[MAXN],R[MAXN];
int tagA[MAXN],tagB[MAXN],tagL[MAXN];
struct Query{
    int l,r,A,B;
}Q[M];
int stk[M<<1];int top=0;
#define ls (u<<1)
#define rs (u<<1|1)
void build(int u,int l,int r){
    if(l==r) {L[u]=stk[l-1]+1,R[u]=stk[r];return;}
    int mid=l+r>>1;
    build(ls,l,mid);build(rs,mid+1,r);
    L[u]=L[ls],R[u]=R[rs];return;
}
ll likegcd(ll n,ll a,ll b,ll c){
    if(c<=a||c<=b) return (a/c*(n*(n+1)/2)+b/c*(n+1)+likegcd(n,a%c,b%c,c));
    if(!a||!n) return 0;
    ll m=(a*n+b)/c;
    return n*m-likegcd(m-1,c,c-b-1,a);
}
inline void Cover(int u,int l,int r,int L,int A,int B){
    int len=r-l+1;
    sum[u]=1ll*(l+r-(L<<1)+2)*len/2*A-1ll*B*(likegcd(r-L+1,A,0,B)-likegcd(l-L,A,0,B));
    tagA[u]=A,tagB[u]=B;tagL[u]=L;
    return;
}
inline void push_down(int u){
    if(tagL[u]) {
        int lson=ls,rson=rs;
        Cover(lson,L[lson],R[lson],tagL[u],tagA[u],tagB[u]);
        Cover(rson,L[rson],R[rson],tagL[u],tagA[u],tagB[u]);
        tagL[u]=tagA[u]=tagB[u]=0;
    }
    return;
}
void Modify(int u,int l,int r,int NL,int NR,int LS,int A,int B){
    if(l>=NL&&r<=NR) return Cover(u,L[u],R[u],LS,A,B);
    int lson=ls,rson=rs;push_down(u);
    int mid=l+r>>1;
    if(mid>=NL) Modify(lson,l,mid,NL,NR,LS,A,B);
    if(mid< NR) Modify(rson,mid+1,r,NL,NR,LS,A,B);
    sum[u]=sum[lson]+sum[rson];
    return;
}
ll query(int u,int l,int r,int L,int R){
    if(l>=L&&r<=R) return sum[u];
    push_down(u);
    int mid=l+r>>1;
    if(mid>=R) return query(ls,l,mid,L,R);
    if(mid< L) return query(rs,mid+1,r,L,R);
    return query(ls,l,mid,L,mid)+query(rs,mid+1,r,mid+1,R);
}
int main()
{
    init(n);init(q);
    for(int i=1;i<=q;++i){//区间的离散化是把 l-1 和 r 离散
        int op;init(op);
        if(op==1){
            int l,r,A,B;
            init(l);init(r);init(A);init(B);
            Q[i].l=l,Q[i].r=r,Q[i].A=A,Q[i].B=B;
            stk[++top]=l-1,stk[++top]=r;
        }
        else {
            int l,r;init(l);init(r);
            Q[i].l=l,Q[i].r=r;
            stk[++top]=l-1,stk[++top]=r;
            Q[i].A=-1;
        }
    }
    stk[0]=0;
    sort(stk+1,stk+1+top);top=unique(stk+1,stk+1+top)-stk-1;
    build(1,1,top);
    for(int i=1;i<=q;++i){
        if(Q[i].A!=-1){
            int l=Q[i].l,r=Q[i].r,A=Q[i].A,B=Q[i].B;
            int Li=lower_bound(stk+1,stk+1+top,l-1)-stk+1;
            int Ri=lower_bound(stk+1,stk+1+top,r)-stk;
            Modify(1,1,top,Li,Ri,l,A,B);
        }
        else printf("%lld
",query(1,1,top,lower_bound(stk+1,stk+1+top,Q[i].l-1)-stk+1,lower_bound(stk+1,stk+1+top,Q[i].r)-stk));
    }
}

以上是关于LuoguP4433[COCI2009-2010#1] ALADIN(含类欧几里得算法推导)的主要内容,如果未能解决你的问题,请参考以下文章

Centos 7 Puppet Enterprise 安装 - 4433 端口上的连接被拒绝

HDU4433 Locker(DP)

省赛复习 最小生成树专题

HDU 4433 locker(DP)( UVA 1631 - Locker)

[COCI]coci2015/2016 nekameleoni

[SinGuLaRiTy] COCI 2016~2017 #5