• Codeforces Round #736 (Div. 2)


    A,B,C就不说了,又被D题卡住了.....
    感觉怎么说呢,就是题解中的三个提示都已经想到了,就是不知道该怎么解决....

    D. Integers Have Friends

    简述题意:题目要求你找一个区间([l,r])使得(a_l)%m=(a_{l+1})%m=...=(a_r)%m。且m>=2,要求能找到的最大区间是多少。
    看到取模我的想法就是将其最原本的式子写(a_l)=c1m+k,(a_{l+1})=c2m+k,...,(a_r)=cn*m+k,考虑他们之间的关系,发现有个相同的余数k,我们做个差分数组,这样他们的k就都抵消了..于是我们惊奇的发现m是他们差分数组的gcd,这样后我们就知道一个区间合法当且仅当这个区间的差分数组的gcd>=2,这样的话我们直接在原数组上做差分。长度变为n-1,题目转化成,我们需要找到一个区间,使得他们的gcd>=2,且长度最大。.....
    昨天到这就gg了....找区间的话,无外乎就是固定左端点最最远的右端点,尺取法等操作,考虑尺取法的话确实发现右端点是单调递增的,但左端点移动时我们无法去除左端点的影响,这个方法暂时告歇...接着考虑我们学过的一些数据结构,gcd符合区间加法的原则(广义上的区间加法,即知道了左区间的gcd,右区间的gcd,我们就可以计算出整个区间的gcd了)。这样的话我们就可以用各种结构进行优化试试,首先我们右端点是朴素的从左向右扫的,倍增能不能呢?好像可以,只需要预处理一下就可以了。线段树作为区间之王,行不行?貌似也可以,比如说给定一个l,我们先进入l这个叶子节点,等回溯时,一点点尝试将区间往上叠加,先将右区间全部叠加上看行不行,不行的话再向下搜寻就大概可以了(我码一下试试.).

    倍增+ST表
     
         //不等,不问,不犹豫,不回头.
    #include
    #define _ 0
    #define ls p<<1
    #define db double
    #define rs p<<1|1
    #define RE register
    #define P 1000000007
    #define ll long long
    #define INF 1000000000
    #define get(x) x=read()
    #define PLI pair
    #define PII pair
    #define ull unsigned long long
    #define put(x) printf("%d
    ",x)
    #define putl(x) printf("%lld
    ",x)
    #define rep(x,y,z) for(RE int x=y;x<=z;++x)
    #define fep(x,y,z) for(RE int x=y;x>=z;--x)
    #define go(x) for(RE int i=link[x],y=a[i].y;i;y=a[i=a[i].next].y)
    using namespace std;
    const int N=2e5+10;
    int n; 
    ll a[N],b[N],f[N][22];//f[i][j]表示从第i个数开始,一共2^j个数的gcd的值. 
    

    inline ll read()
    {
    ll x=0,ff=1;
    char ch=getchar();
    while(!isdigit(ch)) {if(ch=='-') ff=-1;ch=getchar();}
    while(isdigit(ch)) {x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
    return x*ff;
    }

    inline ll gcd(ll a,ll b) {return b?gcd(b,a%b):a;}

    int main()
    {
    // freopen("1.in","r",stdin);
    int get(T);
    while(T--)
    {
    get(n);
    rep(i,1,n) get(a[i]);
    rep(i,1,n-1) b[i]=abs(a[i+1]-a[i]); //注意这里用绝对值,防止出现负数.
    rep(i,1,n-1) f[i][0]=b[i];
    rep(j,1,20)
    rep(i,1,n-1)
    {
    if(i+(1<<j)-1>n-1) break;
    f[i][j]=gcd(f[i][j-1],f[i+(1<<(j-1))][j-1]);
    }
    ll ans=1;
    rep(i,1,n-1)//从i为起点开始向后找.
    {
    ll gcc=f[i][0],now=i+1;
    if(gcc<2) continue;
    fep(j,20,0)
    {
    if(now+(1<<j)-1>n-1) continue;
    if(gcd(gcc,f[now][j])>=2)
    {
    gcc=gcd(gcc,f[now][j]);
    now=now+(1<<j);
    }
    }
    ans=max(ans,now-i+1);
    }
    put(ans);
    }
    return (0_0);
    }
    //以吾之血,铸吾最后的亡魂.

    线段树由于有巨大的常数,就T掉了,不过打出来还是比较考验码力的...
    线段树上二分
     
        //不等,不问,不犹豫,不回头.
    #include
    #define _ 0
    #define ls p<<1
    #define db double
    #define rs p<<1|1
    #define RE register
    #define P 1000000007
    #define ll long long
    #define INF 1000000000
    #define get(x) x=read()
    #define PLI pair
    #define PII pair
    #define ull unsigned long long
    #define put(x) printf("%d
    ",x)
    #define putl(x) printf("%lld
    ",x)
    #define rep(x,y,z) for(RE int x=y;x<=z;++x)
    #define fep(x,y,z) for(RE int x=y;x>=z;--x)
    #define go(x) for(RE int i=link[x],y=a[i].y;i;y=a[i=a[i].next].y)
    using namespace std;
    const int N=2e5+10;
    int n; 
    ll a[N],b[N];
    struct Tree
    {
        int l,r;
        ll dat;//存一个区间所有值得gcd. 
        #define l(p) t[p].l
        #define r(p) t[p].r
        #define d(p) t[p].dat
    }t[N<<2];
    

    inline ll read()
    {
    ll x=0,ff=1;
    char ch=getchar();
    while(!isdigit(ch)) {if(ch=='-') ff=-1;ch=getchar();}
    while(isdigit(ch)) {x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
    return x*ff;
    }

    inline ll gcd(ll a,ll b){return b?gcd(b,a%b):a;}

    inline void build(int p,int l,int r)
    {
    l(p)=l;r(p)=r;
    if(l==r) {d(p)=b[l];return;}
    int mid=l+r>>1;
    build(ls,l,mid);
    build(rs,mid+1,r);
    d(p)=gcd(d(ls),d(rs));
    }

    inline int ask(int p,int k,ll &v)
    {
    if(v0)
    {
    if(l(p)
    r(p))
    {
    v=d(p);
    return l(p);
    }
    int mid=l(p)+r(p)>>1,id=0;
    if(k<=mid) id=ask(ls,k,v);
    else id=ask(rs,k,v);
    if(idr(ls))
    {
    if(gcd(v,d(rs))>=2)
    {
    v=gcd(v,d(rs));
    return r(rs);
    }
    else return ask(rs,k,v);
    }
    else return id;
    }
    else
    {
    if(gcd(v,d(p))>=2)
    {
    v=gcd(v,d(p));
    return r(p);
    }
    else if(l(p)
    r(p)) return l(p)-1;
    else if(gcd(v,d(ls))<2) return ask(ls,k,v);
    else
    {
    v=gcd(v,d(ls));
    return ask(rs,k,v);
    }
    }
    }

    int main()
    {
    // freopen("1.in","r",stdin);
    int get(T);
    while(T--)
    {
    get(n);
    rep(i,1,n) get(a[i]);
    rep(i,1,n-1) b[i]=abs(a[i+1]-a[i]);
    if(n-1>=1) build(1,1,n-1);
    ll ans=1;
    rep(i,1,n-1)//枚举每一个左端点。
    {
    if(b[i]<2) continue;
    ll v=0;
    ll p=ask(1,i,v);
    ans=max(ans,p-i+2);
    }
    putl(ans);
    }
    return 0;
    }
    //以吾之血,铸吾最后的亡魂.

    E. The Three Little PigsE. The Three Little Pigs

    我脑子有坑...还是读错题了,...每次袭击都一定会袭击x个...我以为每个方案都要袭击若干次...
    既然如此那就好办了,我们可以枚举大灰狼在哪一分钟袭击了,然后在这一分钟选出x个就行了,具体来说对于一个询问x而言答案为(sum_{i=0}^{n} C_{3*i}^x)
    可这里有q个询问...怎么搞....一般来说,遇到这种数学式子的问题且是组合数,我们需要找到递推式来快速找到答案。因为组合数的种种优美性质,求多个组合数的和时总是能找到递推预处理,然后O(1)的输出答案。一般的我们可以考虑我们已经知道了当前数x的答案,我们思考怎么根据已有的数据快速得到x+1的答案,具体的我们令(f(x,j)表示sum_{i=1}^{n} C_{3*i+j}^{x}),显然(f(x,0))就是每个x的答案,由于(C_{3*i+j}^{x}=C_{3*i+j-1}^{x}+C_{3*i+j-1}^{x-1})
    所以有(f(x,1)=f(x,0)+f(x-1,0))
    (f(x,2)=f(x,1)+f(x-1,1))
    通过简单的容斥(一点都不简单...)发现(f(x,0)+f(x,1)+f(x,2)=sum_{i=1}^{n}C_{3*i+0}^{x}+sum_{i=1}^{n}C_{3*i+1}^{x}+sum_{i=1}^{n}C_{3*i+2}^{x}=sum_{i=1}^{n}(C_{3*i+0}^{x}+C_{3*i+1}^{x}+C_{3*i+2}^{x})=sum_{i=3}^{3*n+2} C_{i}^{x})
    到这里就停止了吗?不,路还在前方,我们把式子展开一下试试:(C_x^x+C_{x+1}^x+C_{x+2}^x+...+C_{3*n+2}^x)我们可以发现(C_x^x+C_{x+1}^x=C_{x+1}^{x+1}+C_{x+1}^x=C_{x+2}^{x+1})哇,这样可以一直合并下去啊!最后的结果呢,就是(C_{3*n+3}^{x+1})
    经过一番努力我们得到了(f(x,0)+f(x,1)+f(x,2)=C_{3*n+3}^{x+1})
    结合之上的两条(f(x,1)=f(x,0)+f(x-1,0))
    (f(x,2)=f(x,1)+f(x-1,1))
    我们找出递推式:(f(x,0)=frac{C_{3n+3}^{x+1}-f(x-1,1)-2*f(x-1,0)}{3})
    (f(x,1)=f(x,0)+f(x-1,0))
    (f(x,2)=f(x,1)+f(x-1,1))
    初值(f(0,0)=f(0,1)=f(0,2)=n+1)
    目标为(ans(x)=f(x,0))经过离线就可以O(n)解决问题了。
    最后来个总结:题目要求(sum_{i=0}^{n} C_{3*i}^{x})的值,我们肯定不能直接求,显然要利用组合数的一些公式进行化简,但观察组合数的x是不变的,变化的是3*i,我们一方面可以想到他们都是相差3的,我们可以将其中的1,2补齐变成连续的,一方面可以思考组合数的化简公式(C_n^m=C_{n-1}^{m-1}+C_{n-1}^m)可以发现下面的n都要变成n-1,这就提示我们要设出(f(x,j))(sum C_{3*i+j}^x)这样的话我们就可以找到关系了。至于初值为什么要赋成n+1,i=0确实要考虑。

    查看代码
     
      //不等,不问,不犹豫,不回头.
    #include
    #define _ 0
    #define ls p<<1
    #define db double
    #define rs p<<1|1
    #define ll long long
    #define INF 1000000000
    #define get(x) x=read()
    #define PLI pair
    #define PII pair
    #define ull unsigned long long
    #define put(x) printf("%d
    ",x)
    #define putl(x) printf("%lld
    ",x)
    #define rep(x,y,z) for(int x=y;x<=z;++x)
    #define fep(x,y,z) for(int x=y;x>=z;--x)
    #define go(x) for(RE int i=link[x],y=a[i].y;i;y=a[i=a[i].next].y)
    using namespace std;
    const int N=1e6+10,P=1e9+7;
    int n,m;
    ll jc[3*N],inv_jc[3*N],f[N*3][3];
    

    inline int read()
    {
    int x=0,ff=1;
    char ch=getchar();
    while(!isdigit(ch)) {if(ch=='-') ff=-1;ch=getchar();}
    while(isdigit(ch)) {x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
    return x*ff;
    }

    inline ll power(ll a,int b)
    {
    ll ans=1;
    while(b)
    {
    if(b&1) ans=ansa%P;
    b>>=1;
    a=a
    a%P;
    }
    return ans%P;
    }

    inline void prework()
    {
    jc[0]=1;inv_jc[0]=1;
    int t=3n+3;
    rep(i,1,t) jc[i]=jc[i-1]
    i%P;
    inv_jc[t]=power(jc[t],P-2);
    fep(i,t-1,1) inv_jc[i]=inv_jc[i+1]*(i+1)%P;
    }

    inline ll C(int n,int m)
    {
    if(m>n) return 0;
    return jc[n]inv_jc[m]%Pinv_jc[n-m]%P;
    }

    int main()
    {
    //freopen("1.in","r",stdin);
    get(n);get(m);prework();
    f[0][0]=f[0][1]=f[0][2]=n+1;
    ll inp=power(3,P-2);
    rep(i,1,3n)
    {
    f[i][0]=((C(3
    n+3,i+1)-f[i-1][1]-2f[i-1][0])%P+P)%Pinp%P;
    f[i][1]=(f[i][0]+f[i-1][0])%P;
    f[i][2]=(f[i][1]+f[i-1][1])%P;
    }
    rep(i,1,m)
    {
    int get(x);
    putl(f[x][0]);
    }
    return (0_0);
    }
    //以吾之血,铸吾最后的亡魂.

    PS:此题卡常,请将常数尽可能的减小,当处理1-n的阶乘及逆元时,可以先将n的阶乘的逆元求出来,之后就可以O(n)的预处理逆元了。
  • 相关阅读:
    数组的处理
    bootstrap 列的排序
    bootstrap 偏移
    mysql数据库创建、删除数据库
    Spring 加载配置文件的方式
    MyBatis DAO层传递参数到mapping.xml 几种方式
    Python 正则表达式
    Python 之定时器
    Python学习
    获取鼠标经过位置的X、Y坐标
  • 原文地址:https://www.cnblogs.com/gcfer/p/15112730.html
Copyright © 2020-2023  润新知