zoukankan      html  css  js  c++  java
  • 清北2017夏令营考试day1

    清北夏令营考试day1


    送分题(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。

     

    方法:

            固定左端点,随着右端点的移动,并更新ai最小值。

     1 for (L=1; L<=n; L++)
     2 {
     3   MIN=a[L]; sum=0;
     4   for (R=L; R<=n; R++)
     5   {
     6     MIN=min(MIN,a[R]);
     7     sum+=a[R];
     8     if (R!=n || L!=1) 
     9       ans=max(ans,sum+max(0,p-MIN)); else 
    10       ans=max(ans,sum+p-MIN);
    11   }
    12 }
    View Code

    枚举一个位置 将它的值改成P 用O(n)的最大子段和直接做。

            也可以O(n),用栈优化(然而作为蒟蒻的我并不会)。

    标程:

     1 #include <cstdio>
     2 #include <algorithm>
     3 #include <cstring>
     4 #include <iostream>
     5 #include <string>
     6 #include <vector>
     7 #include <set>
     8 #include <map>
     9 using namespace std;
    10 
    11 long long ans;
    12 
    13 int T, n, p, a[1100];
    14 
    15 void doit() {
    16     long long p = 0, s = 0;
    17     for (int i = 1; i <= n; i++) {
    18         s += a[i];
    19         ans = max(ans, s - p);
    20         p = min(p, s);
    21     }
    22 }
    23 
    24 int main() {
    25     freopen("songfen.in","r",stdin);
    26     freopen("songfen.out","w",stdout);
    27         scanf("%d%d", &n, &p);
    28         for (int i = 1; i <= n; i++)
    29             scanf("%d", &a[i]);
    30         ans = -1e18;
    31         for (int i = 1; i <= n; i++) {
    32             int t = a[i];
    33             a[i] = p;
    34             doit();
    35             a[i] = t;
    36         }
    37         printf("%I64d
    ", ans);
    38     return 0;
    39 }
    View Code

     

    树状数组(lowbit)

    Time Limit:1000ms   Memory Limit:128MB

     

    题目描述:

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

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

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

    现在LYK有了n个数字,元组(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。

    方法:

             ①:trie树。

                    对于每个数ai,求出有多少aj,是lowbit(ai,aj)=1,lowbit(ai,aj)=2,lowbit(ai,aj)=3.。。。。。。

                   (然而,我不会啊。。。。。。)

             ②:分治。

                    lowbit(x,y),把x、y都分解成二进制,奇数二进制末位是1,偶数是0。

     

     若要lowbit(x^y)=1,则需要末位不相同,即要求一个奇数和一个偶数搭配。

     我们把所有的奇数归到一类放在左边,假设有x个;把所有的偶数放到右边,假设有y个。

     则一共有x*y*2对lowbit(x,y)=1,贡献值为x*y*2*1;

     

     若要lowbit(x^y)=2,则需要末位x和y全奇,或者全偶。并且倒数第二位两者不相同。

     即在已经看过第一位的时候,再分别看末位为0和末位为1的两组(即奇数和偶数),

     在其中的一组中,再次把他们分成倒数第二位为0,倒数第二位为奇的两组(即奇数和偶数)。

     假设此时倒数第一位全是奇数的条件下,倒数第二位奇数有a个,偶数有b个,

     则它的贡献为a*b*2*2(末位为偶数也是这样)。

     

     如果在要lowbit(x^y)=4,则要在末位,倒数第二位相同的的情况下,在进行细分。

     这样,一个分治的思路就体现出来了。

     

     分治完之后, 二元组一个在左边,另一个在右边,这样的贡献是很容易统计的。
     对于每一个数,它只在最多31层中被分治到,总时间复杂度是n*31。

     

     对于每一个数ai,我们求出有多少aj,
     使得lowbit(ai^aj)=1
     lowbit(ai^aj)=2
     。。。。。。
     lowbit(ai^aj)=2^29

     

    标程(分治):

     

     1 #include <cmath>
     2 #include <cstdio>
     3 #include <cstdlib>
     4 #include <iostream>
     5 #include <algorithm>
     6 #include <string>
     7 #include <cstring>
     8 #include <vector>
     9 #include <set>
    10 #include <map>
    11 using namespace std;
    12 int a[100005],T,_,i,n,b[100005];
    13 long long ans;
    14 const int MOD=1000000007;
    15 void work(int n,int t)
    16 {
    17     if (n<=1 || t>29) return;
    18     int L=0;
    19     for (int i=1; i<=n; i++) if (a[i]&(1<<t)) b[++L]=a[i];int R=L;
    20     for (int i=1; i<=n; i++) if (!(a[i]&(1<<t))) b[++R]=a[i];
    21     for (int i=1; i<=n; i++) a[i]=b[i];
    22     ans+=1ll*(1<<t)*L*(n-L);
    23     work(L,t+1); int p=0;
    24     for (int i=L+1; i<=R; i++) a[++p]=a[i];
    25     work(p,t+1);
    26 }
    27 int main()
    28 {
    29     freopen("lowbit.in","r",stdin);
    30     freopen("lowbit.out","w",stdout);
    31     scanf("%d",&n);
    32     for (i=1; i<=n; i++) scanf("%d",&a[i]);
    33     work(n,0);
    34     printf("%I64d
    ",ans*2%MOD);
    35     return 0;
    36 }
    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)

    第一行两个数k,c。

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

     

    输出格式(fangak.out)

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

     

    输入样例

    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

    判断答案是否<=mid,

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

    k= 0 ~ i-1,dp[i]=dp[k]+(i-k-1),k+1~i-1都被改变了

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

    标程:

     

     1 #include <bits/stdc++.h>
     2 using namespace std;
     3 int dp[2005],n,m,l,r,mid,a[2005],i;
     4 bool OK(int x)
     5 {
     6     dp[0]=0;
     7     for (int i=1; i<=n; i++)
     8     {
     9         dp[i]=1<<30;
    10         for (int j=0; j<i; j++)
    11           if(j==0 || abs(a[i]-a[j])<=(long long)x*(i-j)) dp[i]=min(dp[i],dp[j]+i-j-1);
    12     }
    13     for (int i=1; i<=n; i++)
    14       if (dp[i]+n-i<=m) return true;
    15     return false;
    16 }
    17 int main()
    18 {
    19     freopen("fangak.in","r",stdin);
    20     freopen("fangak.out","w",stdout);
    21     scanf("%d%d",&n,&m);
    22     for (i=1; i<=n; i++) {scanf("%d",&a[i]); assert(-1000000000<=a[i] && a[i]<=1000000000);}
    23     l=0; r=2000000000; mid=(l+r)/2;
    24     while (l<=r)
    25     {
    26         if (OK(mid)) {r=mid-1; mid=((long long)l+r)/2;} else
    27         {
    28             l=mid+1;
    29             mid=((long long)l+r)/2;
    30         }
    31     }
    32     cout<<l<<endl;
    33     return 0;
    34 }
    View Code

     

  • 相关阅读:
    数组和字符串的一些常用方法
    函数记忆
    实现一个clone函数,对javascript中的5种数据类型进行值复制
    visual studio 2010问题修复
    url模块、path模块、querystring模块
    静态资源文件使用
    路由表机制
    解决Node.js服务器启动后在浏览器访问时中文显示乱码
    Node.js 初步了解
    Node.js 简介与安装
  • 原文地址:https://www.cnblogs.com/gjc1124646822/p/7182089.html
Copyright © 2011-2022 走看看