• 2017 清北济南考前刷题Day 6 afternoon


    期望得分:100+100+30=230

    实际得分:

    正解:

    枚举最高的位,这一位m是1但实际用了0

    然后剩余的低位肯定是 正数就用1,负数用0

    考场思路:数位DP

    #include<cstdio>
    #include<cstring>
    #include<iostream>
    #include<algorithm>
    
    using namespace std;
    
    typedef long long LL;
    
    #define N 100001
    
    int a[N];
    
    char s[N];
    
    LL dp[N][2];
    
    void read(int &x)
    {
        x=0; int f=1; char c=getchar();
        while(!isdigit(c)) { if(c=='-') f=-1; c=getchar(); }
        while(isdigit(c)) { x=x*10+c-'0'; c=getchar(); }
        x*=f;
    }
    
    LL dfs(int dep,int num,bool lim)
    {
        if(!dep) return 0;
        if(!lim && dp[dep][num]!=-1) return dp[dep][num];
        int up= lim ? s[dep]-'0' : 1; LL res=0;
        res=dfs(dep-1,0,lim && 0==s[dep]-'0');
        if(up) res=max(res,a[dep]+dfs(dep-1,1,lim && 1==s[dep]-'0'));
        if(!lim) dp[dep][num]=res;
        return res;
    }
    
    int main()
    {
        freopen("maximum.in","r",stdin);
        freopen("maximum.out","w",stdout);
        int n;
        read(n);
        for(int i=1;i<=n;i++) read(a[i]);
        scanf("%s",s+1);
        memset(dp,-1,sizeof(dp));
        cout<<dfs(n,0,1);
    }
    View Code

    二分+DP

    dp[i] 表示 到第i个数, 在满足条件(任意两个相邻的数,差<=mid)的情况下,并且i没有被改变,最少改变多少数字。

    状态转移: dp[i]=dp[k]+(i-k-1)    k=0~i-1  表示 k+1~i-1 都被改变了

    转移条件:mid*(k-i)>=abs(a[k]-a[i])  k~i这段区间能满足条件

     

    只管修改了多少个数,至于改成了什么,不关心

    最大的差最小,就是让数均匀分布,那么二分最大的差,条件就是差*个数>= | 区间右边-左边 |

    也就是假设区间左右端点都不改变,而区间内部都改变    

    区间内部都改变是最差的情况,他会随着区间左端点的移动而变小 

    #include<cstdio>
    #include<iostream>
    #include<algorithm>
    
    using namespace std;
    
    #define N 1001
    
    int dp[N],a[N];
    
    int n,k;
    
    void read(int &x)
    {
        x=0; char c=getchar();
        while(!isdigit(c)) c=getchar();
        while(isdigit(c)) { x=x*10+c-'0'; c=getchar(); }
    }
    
    bool check(int mid)
    {
        for(int i=2;i<=n;i++)
        {
            dp[i]=i-1;
            for(int j=1;j<i;j++)
                if(abs(a[i]-a[j])<=mid*(i-j)) dp[i]=min(dp[i],dp[j]+i-j-1);
        }
        int mi=dp[n];
        for(int i=1;i<n;i++) mi=min(mi,dp[i]+n-i);
        return mi<=k;
    }
    
    int main()
    {
        freopen("minimum.in","r",stdin);
        freopen("minimum.out","w",stdout);
        read(n); read(k);
        for(int i=1;i<=n;i++) read(a[i]);
        int l=0,r=0,mid,ans;
        for(int i=2;i<=n;i++) r=max(r,abs(a[i]-a[i-1]));
        ans=r;
        while(l<=r)
        {
            mid=l+r>>1;
            if(check(mid)) ans=mid,r=mid-1;
            else l=mid+1;
        }
        printf("%d",ans);
    }
    View Code

    将A看做+y,B看做-x,问题转化为 在[L,R] 内 求 最长和为0的区间

    记录 前缀和 a[i] ,若 a[v]-a[u]=0 ,则 [u+1,v] 为 一个和为0的区间

    将所有的前缀和离散化为c[i]

    pos[i]  表示 当前 离散化 后为i的数 第一次 出现在pos[i]

    假设当前在位置j,

    若 pos[c[j]] 之前被更新过了,那么 [ pos[c[j]],j ] 就是一个 和为0的区间 ,且是以j为右端点的最长的

    若没有被更新,用j更新pos[c[j]]

     

    将 字符串分为根号n 块 

    用上述方法在n*根号n 时间 内 预处理完 这三个数组:

    two[i][j]  i,j <= 根号n ,第i个块到第j个块之间 和为0 的最长区间

    l[i][j]  i <= 根号 n ,j <=n,区间[ 第i块的第一个位置,j ]  和为0的最长区间

    r[i][j] i <= 根号 n ,j <=n,区间[ j+1,第i块的最后一个位置 ] 和为0的最长区间

     

    对于 一个询问 L,R

    由四部分更新

     

    设L所在块为bL,R所在块为bR

    ① 最优解 在 [L,R] 完整的块内 即 two[bL+1][bR-1]

    ② 最优解的左端点 不在[L,R] 完整块内,右端点在[L,R]完整块内 即 r[bR-1][L-1]

    ③ 最优解的左端点 在[L,R]完整块内,右端点不在[L,R] 完整块内,即l[bL+1][R]

    ④ 最优解 的左右端点都不在[L,R] 完整块内 ,

    那么 用上面的方法 枚举 左边不在完整块内的部分,枚举右边不在完整块内的部分,O(2*根号n) 求解 

    #include<cmath>
    #include<cstdio>
    #include<cstring>
    #include<iostream>
    #include<algorithm>
    
    using namespace std;
    
    #define N 100001
    
    char s[N];
    
    int a[N],b[N],c[N];
    
    int two[320][320],l[320][N],r[320][N];
    
    int pos[N];
    
    int use[N];
    
    void read(int &x)
    {
        x=0; char c=getchar();
        while(!isdigit(c)) c=getchar();
        while(isdigit(c)) { x=x*10+c-'0'; c=getchar(); }
    }
    
    int getgcd(int a,int b) { return !b ? a : getgcd(b,a%b); }
    
    int main()
    {
        freopen("sequence.in","r",stdin);
        freopen("sequence.out","w",stdout);
        int n,x,y;
        read(n); read(x); read(y);
        int g=getgcd(x,y); x/=g; y/=g;
        scanf("%s",s+1);
        for(int i=1;i<=n;++i) 
        {
            if(s[i]=='A') a[i]=a[i-1]+y;
            else a[i]=a[i-1]-x;
            b[i]=a[i];
        }
        sort(b+1,b+n+1);
        int tot=unique(b+1,b+n+1)-b-1;
        for(int i=0;i<=n;++i) c[i]=lower_bound(b+1,b+tot+1,a[i])-b;
        int siz=sqrt(n),mx,m=siz;
        int cnt=0;
        memset(pos,-1,sizeof(pos));
        for(int i=1,k=1;i<=n;i+=siz,++k)
        {
            mx=cnt=0;
            for(int j=i,kk=k;j<=n;j+=siz,++kk) 
            {
                for(int h=0;h<=siz;++h)
                {
                    if(pos[c[j+h-1]]!=-1) mx=max(mx,j+h-1-pos[c[j+h-1]]);
                    else pos[c[j+h-1]]=j+h-1;
                    use[++cnt]=c[j+h-1];
                }
                two[k][kk]=mx;
            }
            for(int i=1;i<=cnt;++i) pos[use[i]]=-1;
        }
        for(int i=1,k=1;i<=n;i+=siz,++k)
        {
            cnt=0;
            for(int j=i;j<=n;++j)
            {
                if(pos[c[j]]!=-1) l[k][j]=max(l[k][j-1],j-pos[c[j]]);
                else l[k][j]=l[k][j-1],pos[c[j]]=j;
                use[++cnt]=c[j];
            }
            for(int j=1;j<=cnt;++j) pos[use[j]]=-1;
        }
        for(int i=siz,k=1;i<=n;i+=siz,++k)
        {
            cnt=0;
            for(int j=i;j>=0;--j)
            {
                if(pos[c[j]]!=-1) r[k][j]=max(r[k][j+1],pos[c[j]]-j);
                else r[k][j]=r[k][j+1],pos[c[j]]=j;
                use[++cnt]=c[j];
            }
            for(int j=1;j<=cnt;++j) pos[use[j]]=-1;
        }
        int q;
        read(q);
        int L,R; int bL,bR; int ans; 
        while(q--)
        {
            read(L); read(R);
            bL=(L-1)/siz+1; bR=(R-1)/siz+1;
            ans=0;
            if(bR-bL<2)
            {
                cnt=0;
                for(int i=L-1;i<=R;++i)
                {
                    if(pos[c[i]]!=-1) ans=max(ans,i-pos[c[i]]); 
                    else pos[c[i]]=i;
                    use[++cnt]=c[i];
                }
                for(int i=1;i<=cnt;++i) pos[use[i]]=-1; 
            }
            else
            {
                ans=two[bL+1][bR-1];
                ans=max(ans,r[bR-1][L-1]);
                ans=max(ans,l[bL+1][R]);
                cnt=0;
                int t=bL*siz;
                for(int i=L-1;i<=t;++i) 
                {
                    if(pos[c[i]]!=-1) ans=max(ans,i-pos[c[i]]);
                    else pos[c[i]]=i;
                    use[++cnt]=c[i]; 
                }
                for(int i=(bR-1)*siz+1;i<=R;++i)
                {
                    if(pos[c[i]]!=-1) ans=max(ans,i-pos[c[i]]);
                    else pos[c[i]]=i;
                    use[++cnt]=c[i];
                }
                for(int i=1;i<=cnt;++i) pos[use[i]]=-1;
            }
            cout<<ans<<'
    ';
        }
    }
    View Code
  • 相关阅读:
    python 希尔排序
    python 选择排序
    python 插入排序
    怎么根据返回数量,来渲染点赞星星的个数变高量?
    avalon $computed不起作用?
    移动端点击事件兼容问题,在pc端可以点,在手机上不可以点
    移动端弹出遮罩层禁止页面滚动,遮罩里面的框允许滚动如何实现。
    移动端,如何禁止弹窗下面内容滚动/页面滚动
    avalon数据已更新,视图未更新的bug修复
    Python 导入分部分项前的 数据处理
  • 原文地址:https://www.cnblogs.com/TheRoadToTheGold/p/7775009.html
Copyright © 2020-2023  润新知