zoukankan      html  css  js  c++  java
  • noip模拟赛 2017.11.8

    送分题(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^2)的代码是真的送分(来自一个数组只开200的蒟蒻的flag)

    O(n)也不是很难,直接看代码

    代码

    O(n^2)
    
    #include <iostream>
    #include <cstring>
    #include <cstdio>
    #include <cmath>
    #include <algorithm>
    #define N 2000
    #define inf 1000000000
    using namespace std;
    
    int n,p,t[N];
    int mx,cnt,ans;
    
    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",&t[i]);
    	ans=-inf;
    	for(int i=1;i<=n;i++)
    	{
    		swap(t[i],p);cnt=mx=t[1];
    		for(int j=2;j<=n;j++)
    		{
    			if(mx<=0) mx=t[j];
    			else mx+=t[j];
    			cnt=max(cnt,mx);
    		}
    		swap(t[i],p);ans=max(ans,cnt);
    	}
    	printf("%d",ans);
    	return 0;
    }
    
    
    O(n)
    #include <iostream>
    #include <cstring>
    #include <cstdio>
    #include <cmath>
    #include <algorithm>
    #define N 2000
    #define inf 1000000000
    using namespace std;
    
    int n,p,ans;
    int f[N][2];//f[i][0/1]分别表示前i个数 是/否 替换过的最大子段和
    
    int main()
    {
    	freopen("songfen.in","r",stdin);
        freopen("songfen.out","w",stdout);
    	
    	scanf("%d%d",&n,&p);ans=-inf;
    	for(int i=1;i<=n;i++)
    	{
    		int x;scanf("%d",&x);
    		f[i][0]=max(f[i-1][0]+x,x);//前i个数均不替换,那么当前最大子段和即为 加/不加 之前的和 
    		f[i][1]=max(p,max(f[i-1][0]+p,f[i-1][1]+x));//替换分为第i位替换或之前替换,再分析 加/不加 之前的和 
    		ans=max(ans,f[i][1]);//必须替换一次 
    	}
    	printf("%d",ans);
    	return 0;
    }
    

    树状数组(lowbit)

    Time Limit:1000ms Memory Limit:128MB

    题目描述

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

    当它遇到一个叫lowbit的函数时有点懵逼。lowbit(x)的意思是将x分解成二进制,它的值就是2k,其中k是最小的满足(x&2k)>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。

    题解

    image

    然而我写的Trie树,思想跟分治应该差不多吧,每次插入的时候更新ans,我没有将30位补全,所以插入前要sort一遍

    代码

    #include <iostream>
    #include <cstring>
    #include <cstdio>
    #include <cmath>
    #include <algorithm>
    #define ll long long
    #define N 4000005
    #define mod 1000000007
    using namespace std;
    
    int n,a[N];
    ll ans;
    
    int d[N][2],sz[N][2],f[N][2],num;
    void insert(int x)
    {
    	int p=0,cnt=0,deep=0;
    	while(true)
    	{
    		int v=x&1;x>>=1;
    		if(d[p][v^1]) ans=(ans+(1<<deep)*sz[p][v^1])%mod;
    		if(v&&cnt) {ans=(ans+(1<<deep)*cnt)%mod;cnt=0;}
    		if(!d[p][v]) d[p][v]=++num;
    		cnt+=f[p][v];sz[p][v]++;
    		if(!x) {f[p][v]++;break;}
    		p=d[p][v];deep++;
    	}
    }
    
    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]);
    	sort(a+1,a+1+n); 
    	for(int i=1;i<=n;i++) insert(a[i]);
    	printf("%lld",ans*2%mod);
    	return 0;
    }
    

    防AK好题(fangak)

    Time Limit:1000ms Memory Limit:128MB

    题目描述

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

    于是,它有意在最后一题为难一下大家。它定义了一个非常复杂的运算。具体的,一开始它有n个数ai。令c表示最大的相邻两个数的差。也就是说c=max{|a[i]−a[i-1]|}(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,-109<=ai<=109。

    题解

    image

    代码

    #include <iostream>
    #include <cstring>
    #include <cstdio>
    #include <cmath>
    #include <algorithm>
    #define ll long long
    #define N 1005
    #define inf 2000000000
    using namespace std;
    
    int n,k,a[N];
    
    int f[N]; 
    bool check(int x,int t)
    {
    	for(int i=2;i<=n;i++)//枚举最后一个不改的数 
    	{
    		f[i]=i-1;//f[i]记录使前i个满足最少需要改几个 
    		for(int j=1;j<i;j++)
    			if(abs(a[i]-a[j])<=(ll)x*(i-j)) f[i]=min(f[i],f[j]+i-j-1);
    	}
    	int cnt=inf;
    	for(int i=1;i<=n;i++) cnt=min(cnt,f[i]+n-i);
    	return cnt<=k;
    }
    
    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]);
    	int l=0,r=inf;
    	while(l<r)
    	{
    		int mid=(l+r)>>1;
    		if(check(mid,k)) r=mid;
    		else l=mid+1;
    	}
    	printf("%d",l);
    	return 0;
    }
  • 相关阅读:
    检测后缀表达式的合法性
    对表达式进行空白符预处理
    字符串替换
    中缀表达式的计算
    Linux shell编程
    Linux常用shell
    [转载]shell脚本编写规范
    [转载]Linux文件类型
    Linux的进程
    Linux进入命令行模式
  • 原文地址:https://www.cnblogs.com/XYZinc/p/7804264.html
Copyright © 2011-2022 走看看