zoukankan      html  css  js  c++  java
  • 2017 济南综合班 Day 1

    送分题(songfen)

    Time Limit:1000ms   Memory Limit:128MB

    题目描述

    LYK喜欢干一些有挑战的事,比如说求区间最大子段和。它知道这个题目有O(n)的做法。于是它想加强一下。

    也就是说,LYK一开始有n个数,第i个数字是ai,它找来了一个新的数字P,并想将这n个数字中恰好一个数字替换成P。要求替换后的最大子段和尽可能大。

    LYK知道这个题目仍然很简单,于是就扔给大家来送分啦~

    注:最大子段和是指在n个数中选择一段区间[L,R](L<=R)使得这段区间对应的数字之和最大。

    输入格式(songfen.in)

        第一行两个数n,P。

        接下来一行n个数ai。

    输出格式(songfen.out)

        一个数表示答案。

    输入样例

    5 3

    -1 1 -10 1 -1

    输出样例

    5

    样例解释

    将第三个数变成3后最大子段和为[2,4]。

    数据范围

    对于30%的数据n<=100。

    对于另外30%的数据ai,P>=0。

    对于100%的数据n<=1000,-1000<=ai,P<=1000。

    Note:提前AK的同学可以想一想O(n)的做法。

    枚举改哪个,再做O(n)的最大子段和。总复杂度:O(n²)

    O(n)做法用栈,不会。。。。。。

    #include<cstdio>
    #include<algorithm>
    #include<cstring>
    using namespace std;
    int n,p,ans,tmp,out=-1e8;
    int dp[1001],a[1001];
    int main()
    {
        freopen("songfen.in","r",stdin);
        freopen("songfen.out","w",stdout);
        scanf("%d%d",&n,&p);
        for(int i=1;i<=n;i++) scanf("%d",&a[i]);
        int x;
        for(int k=1;k<=n;k++)
        {
            tmp=a[k];     a[k]=p; 
            for(int i=1;i<=n;i++)
            {
                x=a[i];
                if(dp[i-1]+x>=x) dp[i]=dp[i-1]+x;
                else dp[i]=x;
                if(dp[i]>ans) ans=dp[i];
            }
            out=max(out,ans);
            a[k]=tmp;
            memset(dp,0,sizeof(dp));
            ans=-1e8;
        }
        printf("%d",out);
    }
    View Code

    错误的O(n):

    dp[i][0]到了第i个,第i个没有改

    dp[i][1]到了第i个,第i个改了

    dp[i][0]=max(dp[i-1][0]+x,dp[i-1][1]+x);
    dp[i][0]=max(dp[i][0],x);
    dp[i][1]=max(dp[i-1][0]+p,p);
    ans=max(ans,dp[i][0]);
    ans=max(ans,dp[i][1]);

    错因:不能保证之修改一次

    第i个改了,第i+1个的不改由第i个的改和不改转移,第i+2个的改由第i+1个的不改转移,这就改了两次

    树状数组(lowbit)

    Time Limit:1000ms   Memory Limit:128MB

    题目描述

          这天,LYK在学习树状数组。

          当它遇到一个叫lowbit的函数时有点懵逼。lowbit(x)的意思是将x分解成二进制,它的值就是 ,其中k是最小的满足(x & )>0的数。(&是二进制中的and运算)

          LYK甚至知道lowbit(x)=(x&-x)。但这并没什么用处。

          现在LYK有了n个数字,为了使自己更好的理解lowbit是什么意思。它想对所有n^2个二元组求lowbit。具体的,对于一个二元组(ai,aj),它的值为lowbit(ai xor aj) (xor表示异或的意思),那么总共有n^2对二元组,LYK想知道所有二元组的值加起来是多少。

          这个答案可能很大,你只需输出这个值对1000000007取模后的结果就可以了。

    输入格式(lowbit.in)
        第一行一个数n,表示有n个这样的数字。

        第二行n个数ai。

    输出格式(lowbit.out)

    一个数表示答案。

    输入样例

    5

    1 2 3 4 5

    输出样例

    32

    数据范围

    对于30%的数据n<=1000。

    对于另外10%的数据ai<=1。

    对于再另外10%的数据ai<=3。

    对于再再另外20%的数据ai<1024。

    对于100%的数据1<=n<=100000,0<=ai<2^30。

    分治

    二进制最后一位是0的放左边,最后一位是1的放右边,对答案的贡献为两者个数的乘积* 2^0

    对于左边的和右边的,分别做:

    二进制倒数第二位是0的放左边,倒数第二位是1的放右边,对答案的贡献为两者个数的乘积* 2^1

    。。。。。。

    边界条件1: 没有数了

    边界条件2:二进制位数>30 (非常重要,他保证了最多分治30层,高效解决应重复出现的数)

    #include<cstdio>
    #define mod 1000000007
    #define N 100001
    
    using namespace std;
    int n;
    int a[N],b[N];
    long long ans;
    void divide(int l,int r, int k)
    {
            if(l>=r || k>29) return;
            int L=l,R=r;
            for(int i=l;i<=r;i++)
                    if(a[i] & (1<<k)) b[L++]=a[i];
                    else b[R--]=a[i];
            ans=(ans+1ll*(L-l)*(r-R)*(1<<k))%mod;
            for(int i=l;i<=r;i++) a[i]=b[i];
            if(l!=L) divide(l,L-1,k+1);
            if(r!=R) divide(R+1,r,k+1);
    }
    int main()
    {
            freopen("lowbit.in","r",stdin);
            freopen("lowbit.out","w",stdout);
            scanf("%d",&n);
            for(int i=1;i<=n;i++) scanf("%d",&a[i]);
            divide(1,n,0);
            printf("%I64d",(ans<<1)%mod);
    }
    View Code

    防AK好题(fangak)

    Time Limit:1000ms   Memory Limit:128MB

    题目描述

    LYK觉得,这场比赛到目前为止,题目都还太简单了。

    于是,它有意在最后一题为难一下大家。它定义了一个非常复杂的运算。具体的,一开始它有n个数ai。令c表示最大的相邻两个数的差。也就是说c=max{| |}(i∈[2,n])。这个值显然是一个常数。

    但是问题来了,LYK为了刁难你们,它想改变其中k个数,也就是说将其中至多k个数变成任意的数,并且LYK要求这么做完后c的值尽可能小。

    输入格式(fangak.in)

    第一行两个数n,k。

    接下来一行n个数表示ai。

    输出格式(fangak.out)

    一个数表示最小的k的值。

    输入样例

    6 3

    1 2 3 7 8 9

    输出样例

    1

    数据范围

    对于20%的数据n<=8。1<=ai<=8。

    对于另外20%的数据k=1。

    对于再另外20%的数据ai一开始是单调递增的。

    对于再再另外20%的数据n<=100。

    对于100%的数据1<=k<=n<=1000,-10^9<=ai<=10^9。

    二分+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这段区间能满足条件

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

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

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

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

    个人感觉:这是一道区间DP

    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #define N 1001
    using namespace std;
    int n,k,maxn=-2e9,minn=2e9;
    int a[N],dp[N+4];
    bool check(int mid)
    {
        dp[0]=0;
        for(int i=1;i<=n;i++)
        {
            dp[i]=n+4;
            for(int j=i-1;j>=0;j--)
              if(!j || mid*(i-j)>=abs(a[i]-a[j])) dp[i]=min(dp[i],dp[j]+i-j-1);
        }
        for(int i=1;i<=n;i++) 
         if(dp[i]+n-i<=k) return true;
        return false;
    }
    int main()
    {
        freopen("fangak.in","r",stdin);
        freopen("fangak.out","w",stdout);
        scanf("%d%d",&n,&k);
        for(int i=1;i<=n;i++) scanf("%d",&a[i]),maxn=max(maxn,a[i]),minn=min(minn,a[i]);
        int l=0,r=maxn-minn,mid,ans;
        while(l<=r)
        {
            mid=l+r>>1;
            if(check(mid)) ans=mid,r=mid-1;
            else l=mid+1;
        } 
        printf("%d",ans);
    } 
    View Code
  • 相关阅读:
    python版本切换及添加路径
    python下载及安装步骤
    day01计算机基础-python解释器-pycharm介绍
    查看pycharm有效期
    对于Makefile的基本使用
    Shell命令整理
    一起来免费听无损高品质音乐吧!
    记录 解决ubuntu16.04 ‘E: 无法获得锁 /var/lib/dpkg/lock-frontend
    Ubuntu16手动安装OpenStack——keystone篇
    python3中的RE(正则表达式)
  • 原文地址:https://www.cnblogs.com/TheRoadToTheGold/p/7266669.html
Copyright © 2011-2022 走看看