清北夏令营考试day1
Posted lonlyn
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了清北夏令营考试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 }
枚举一个位置 将它的值改成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\n", ans); 38 return 0; 39 }
树状数组(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\n",ans*2%MOD); 35 return 0; 36 }
防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
维修中。。。。。。。
标程:
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]=453266144; 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 }
以上是关于清北夏令营考试day1的主要内容,如果未能解决你的问题,请参考以下文章