zoukankan      html  css  js  c++  java
  • [CSP模拟测试43、44]题解

    状态极差的两场。感觉现在自己的思维方式很是有问题。

    (但愿今天考试开始的一刻我不会看到H I J)

    A

    考场上打了最短路+贪心,水了60。

    然而正解其实比那30分贪心好想多了。

    进行n次乘法后的结果一定可以化成$S imes b^n + m imes a$的形式,并且$m$是b的若干次幂(带系数)之和。

    也就是说,$m=frac{T-S imes b^n}{a}$可以写成$b$进制数,当然前提是$T-S imes b^n mod a=0$。

    那么这个b进制数的系数之和其实就是加法操作的次数,这个很好理解。

    枚举乘法次数,然后得到相应的$m$后直接$b$进制拆解,注意要从高次开始。

    #include<cstdio>
    #include<iostream>
    #include<cstring>
    using namespace std;
    typedef long long ll;
    ll S,T,a,b,ans=1e15;
    int main()
    {
        scanf("%lld%lld%lld%lld",&S,&T,&a,&b);
        for(ll n=0,now=1;S*now<=T;n++,now*=b)
        {
            ll m=T-S*now,res=0;
            if(m%a)continue;
            m/=a;
            ll c=now;
            while(m)res+=m/c,m%=c,c/=b;
            res+=n;
            ans=min(ans,res);
        }
        cout<<ans<<endl;
        return 0;
    }
    

    B.

    首先可以想到一个比较暴力的dp:$f[i][j]$表示前$i$个变量乘积为$j$的方案数,枚举上一个结果和当前变量的值$O(p^2)$转移即可。

    正解只理解了思想,但转移方程仍然不是很懂。

    打表很容易发现一个性质:若$gcd(a,P)=gcd(b,P)$,那么$f[i][a]=f[i][b]$。

    显然出题人居心叵测,把两个状态转移方程的条件写反了23333。

    不过话说$varphi(frac{P}{a})$的含义是$a$能代表的数,那方程里为什么要再$ imes a$呢?辣鸡博主不是很明白。

    C.

    设特殊加速器的使用次数为$x$,总费用为$y$,那么$y$关于$x$的函数显然是单谷的,所以可以三分。

    考虑如何在已知$x$的情况下快速求出费用。可以对每个点预处理包含它的区间的最右端点,这个直接开个数组对每个$l_i$标记一下就行。

    每次计算的时候用差分的思想实现区间减法,不过直接暴力循环似乎也可过??

    #include<cstdio>
    #include<iostream>
    #include<cstring>
    using namespace std;
    int read()
    {
        int x=0,f=1;char ch=getchar();
        while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
        while(isdigit(ch))x=x*10+ch-'0',ch=getchar();
        return x*f;
    }
    const int N=1e5+5;
    typedef long long ll;
    int n,m,t,a[N],s[N],L[N],R[N],rm[N],maxh,p[N],v[N];
    ll ans=1e15;
    ll cacl(int x)
    {
        for(int i=1;i<=n;i++)
            p[i]=max(0,a[i]-x);
        ll res=1LL*t*x;
        int yet=0;
        for(int i=1;i<=n;i++)
        {
            yet-=v[i];v[i]=0;
            p[i]=max(0,p[i]-yet);
            res+=p[i];
            yet+=p[i];
            v[rm[i]+1]+=p[i];
        }
        return res;
    }
    
    int main()
    {
        //freopen("9.in","r",stdin);
        n=read();m=read();t=read();
        for(int i=1;i<=n;i++)
            a[i]=read(),maxh=max(maxh,a[i]);
        for(int i=1;i<=m;i++)
        {
            L[i]=read();R[i]=read();
            s[L[i]]=max(s[L[i]],R[i]);
        }
        int now=0;
        for(int i=1;i<=n;i++)
        {
            if(s[i])now=max(now,s[i]);
            rm[i]=now;
            if(now==i)now=0;
        }
        int l=0,r=maxh;
        for(int i=1;i<=n;i++)
            if(!rm[i])l=max(l,a[i]);
        //cout<<l<<endl;
        while(l<=r)
        {
            int mid1=l+(r-l)/3,mid2=l+(r-l)*2/3;
            ll val1=cacl(mid1),val2=cacl(mid2);
            if(val1>=val2)l=mid1+1,ans=min(ans,val2);
            else r=mid2-1,ans=min(ans,val1);
    
        }
        cout<<ans<<endl;
        return 0;
    }
    

    D.

    暴力的基础上加个剪枝就可以。考虑极限情况,如果当前的$gcd$乘上$(n-i+1)$都没法更新ans($i$为枚举的左端点),那么直接break即可。

    实测卡不掉。zkt巨巨证了复杂度,可以做到$O(n log n)$。

    #include<bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    int n,a[100005],st[100005][21],lg[100005]={-1};
    ll ans;
    int read()
    {
        int x=0,f=1;char ch=getchar();
        while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
        while(isdigit(ch))x=x*10+ch-'0',ch=getchar();
        return x*f;
    }
    
    int gcd(int x,int y)
    {
        if(!y)return x;
        return gcd(y,x%y);
    }
    int main()
    {
        n=read();
        for(int i=1;i<=n;i++)
            a[i]=read();
        //ini();
        for(int i=1;i<=n;i++)
        {
            int now=a[i];
            for(int j=i;j<=n;j++)
            {
                now=gcd(now,a[j]);
                if(now==1){ans=max(ans,1LL*(n-i+1));break;}
                if(1LL*now*(n-i+1)<ans)break;
                ans=max(ans,1LL*(j-i+1)*now);
            }
    
        }
    
        cout<<ans<<endl;
        return 0;
    }
    

    E.

    贪心搜索乱搞水过了。严谨的贪心抽时间补。

    #include<bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    const int N=1e5+5;
    int n;
    ll a[N][3],ans=1e15;
    double tim;
    void dfs(int x,ll rmax,ll rmin,ll bmax,ll bmin)
    {
        if(x>1&&(rmax-rmin)*(bmax-bmin)>ans)return ;
        if(x>n)
        {
            ans=min(ans,(rmax-rmin)*(bmax-bmin));
            if((clock()-tim)/1e6>=1.5)printf("%lld
    ",ans),exit(0);
            return ;
        }
        dfs(x+1,max(rmax,a[x][0]),min(rmin,a[x][0]),max(bmax,a[x][1]),min(bmin,a[x][1]));
        dfs(x+1,max(rmax,a[x][1]),min(rmin,a[x][1]),max(bmax,a[x][0]),min(bmin,a[x][0]));
        return ;
    }
    int main()
    {
        scanf("%d",&n);
        for(int i=1;i<=n;i++)
        {
            for(int j=0;j<=1;j++)
                scanf("%lld",&a[i][j]);
            if(a[i][0]<a[i][1])swap(a[i][0],a[i][1]);
        }
        tim=clock();
        dfs(1,0,1e15,0,1e15);
        cout<<ans<<endl;
        return 0;
    }
    

    F.

    线段树优化dp。

    状态定义有些不好想:$f[i][j]$表示进行到第$i$次操作,一个指针在$pos[i]$(题目里给的),另一个在$j$时的最小费用。

    $f[i][j]=min (f[i-1][j]+|p_i-p_{i-1}|)$

    $f[i][p[i-1]]=min (f[i-1][j]+|p_i-j|)$

    暴力转移是$O(n^2)$的。

    #include<bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    int read()
    {
        int x=0,f=1;char ch=getchar();
        while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
        while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();
        return x*f;
    }
    const int N=1e5+5;
    int n,q,p1,p2;
    int pos[N];
    ll ans=1e15,dp[2005][2005];
    int abss(int x)
    {
        return x>0?x:-x;
    }
    int main()
    {
        n=read();q=read();p1=read();p2=read();
        for(int i=1;i<=q;i++)pos[i]=read();
        memset(dp,0x3f,sizeof(dp));
        dp[1][p1]=abss(pos[1]-p2),dp[1][p2]=abss(pos[1]-p1);
        for(int i=2;i<=q;i++)
        {
            for(int j=1;j<=n;j++)
                dp[i][pos[i-1]]=min(dp[i-1][j]+1LL*abss(pos[i]-j),dp[i][pos[i-1]]),
                dp[i][j]=min(dp[i-1][j]+1LL*abss(pos[i]-pos[i-1]),dp[i][j]);
    
        }
    
        for(int i=1;i<=n;i++)
            ans=min(ans,dp[q][i]);
        cout<<ans<<endl;
        return 0;
    }
    

    我们注意到转移1可以用线段树区间加实现,对于转移2先拆绝对值,维护$f[i][j]+j$和$f[i][j]-j$的最小值即可。

    用到的操作:区间修改,区间查询,单点修改。

    注意单点修改要放在区间加后面啊!

    #include<bits/stdc++.h>
    using namespace std;
    const int N=1e5+5;
    typedef long long ll;
    const ll inf=0x3f3f3f3f3f3f3f3f;
    int read()
    {
        int x=0,f=1;char ch=getchar();
        while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
        while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();
        return x*f;
    }
    int n,q,p1,p2;
    int pos[N];
    ll dp[N];
    int abss(int x)
    {
        return x>0?x:-x;
    }
    #define ls(k) (k)<<1
    #define rs(k) (k)<<1|1
    ll a[N<<2],b[N<<2],lz[N<<2];
    void up(int k)
    {
        a[k]=min(a[ls(k)],a[rs(k)]);
        b[k]=min(b[ls(k)],b[rs(k)]);
    }
    void down(int k,int l,int r)
    {
        int mid=l+r>>1;
        lz[ls(k)]+=lz[k];
        a[ls(k)]+=lz[k];b[ls(k)]+=lz[k];
        lz[rs(k)]+=lz[k];
        a[rs(k)]+=lz[k];b[rs(k)]+=lz[k];
        lz[k]=0;
    }
    void build(int k,int l,int r)
    {
        if(l==r)
        {
            if(l==p1)a[k]=abss(pos[1]-p2)+p1,b[k]=abss(pos[1]-p2)-p1;
            else if(l==p2)a[k]=abss(pos[1]-p1)+p2,b[k]=abss(pos[1]-p1)-p2;
            else a[k]=b[k]=inf;
            return ;
        }
        int mid=l+r>>1;
        build(ls(k),l,mid);
        build(rs(k),mid+1,r);
        up(k);
    }
    void add(int k,int l,int r,int L,int R,ll val)
    {
        if(l>r)return ;
        if(L<=l&&R>=r)
        {
            a[k]+=val;b[k]+=val;
            lz[k]+=val;
            return ;
        }
        if(lz[k])down(k,l,r);
        int mid=l+r>>1;
        if(L<=mid)add(ls(k),l,mid,L,R,val);
        if(R<mid)add(rs(k),mid+1,r,L,R,val);
        up(k);
    }
    void update(int k,int l,int r,int pos,ll val,int op)
    {
        if(l==r)
        {
            if(!op)a[k]=min(a[k],val);
            else b[k]=min(b[k],val);
            return ;
        }
        if(lz[k])down(k,l,r);
        int mid=l+r>>1;
        if(pos<=mid)update(ls(k),l,mid,pos,val,op);
        else update(rs(k),mid+1,r,pos,val,op);
        up(k);
    }
    
    ll qmin(int k,int l,int r,int L,int R,int op)
    {
        if(L<=l&&R>=r)return op?b[k]:a[k];
        if(lz[k])down(k,l,r);
        int mid=l+r>>1;
        ll res=inf;
        if(L<=mid)res=min(res,qmin(ls(k),l,mid,L,R,op));
        if(R>mid)res=min(res,qmin(rs(k),mid+1,r,L,R,op));
        return res;
    }
    ll getans(int k,int l,int r)
    {
        if(l==r)return a[k]-l;
        int mid=l+r>>1;
        if(lz[k])down(k,l,r);
        return min(getans(ls(k),l,mid),getans(rs(k),mid+1,r));
    }
    
    int main()
    {
        n=read();q=read();p1=read();p2=read();
        for(int i=1;i<=q;i++)pos[i]=read();
        build(1,1,n);
        for(int i=2;i<=q;i++)
        {
            ll val1=qmin(1,1,n,pos[i],n,0)-pos[i],val2=qmin(1,1,n,1,pos[i],1)+pos[i];
            add(1,1,n,1,n,abss(pos[i]-pos[i-1]));
    
            update(1,1,n,pos[i-1],min(val1,val2)+pos[i-1],0);
            update(1,1,n,pos[i-1],min(val1,val2)-pos[i-1],1);
        }
        cout<<getans(1,1,n)<<endl;
        return 0;
    }
    
  • 相关阅读:
    hdu5728 PowMod
    CF1156E Special Segments of Permutation
    CF1182E Product Oriented Recurrence
    CF1082E Increasing Frequency
    CF623B Array GCD
    CF1168B Good Triple
    CF1175E Minimal Segment Cover
    php 正则
    windows 下安装composer
    windows apache "The requested operation has failed" 启动失败
  • 原文地址:https://www.cnblogs.com/Rorschach-XR/p/11535610.html
Copyright © 2011-2022 走看看