05.26 习题训练三
A - Sorted Adjacent Differences
题意:有n个元素,通过进行重新排列其位置,使得第一二元素差的绝对值<第二三元素差的绝对值<……
思路:对数组进行排序,然后从中间入手,输出中间元素的右边,再输出中间元素的左边,再输出右边……
代码:
1 #include<cstdio> 2 #include<cstdio> 3 #include<cstring> 4 #include<algorithm> 5 #include<iostream> 6 #include<cmath> 7 using namespace std; 8 int main(){ 9 int t; 10 scanf("%d",&t); 11 while(t--){ 12 int a[100005],n; 13 scanf("%d",&n); 14 for(int i=0;i<n;i++){ 15 scanf("%d",&a[i]); 16 } 17 sort(a,a+n); 18 for(int i=n/2-1,j=n/2;i>=0||j<n;i--,j++){ 19 20 if(j<n){ 21 printf("%d ",a[j]); 22 } 23 if(i>=0){ 24 printf("%d ",a[i]); 25 } 26 27 } 28 printf("\n"); 29 } 30 }
B - Powered Addition
题意:有一组数,问通过变化是否能变成不递减数列,规则是:在第x秒的时候可以选择在某个或者某些元素上加上2^(x-1),即在x秒的时候Ai=Ai+2^(x-1)
思路:从前到后遍历找到每个0-i,0<=i<n,最大的数,就是找a[i]和前i-1个元素中最大值的差值,再输出log2(max(n个差值))+1就是最小秒数
wa掉的点/应该注意的:1.不是直接上来找先找到不是递增那堆数列的最大最小值,直接输出log2()+1,这样一来,时间不是最小,比如:1 2 7 3 9,这种纠结非递增之后的3,7是没有任何意义的,并且花费的时间肯定比上述思路的时间多,所以这种想法就不对
代码:
1 #include<cstdio> 2 #include<cstdio> 3 #include<cstring> 4 #include<algorithm> 5 #include<iostream> 6 #include<cmath> 7 using namespace std; 8 int main(){ 9 int t; 10 scanf("%d",&t); 11 while(t--){ 12 int a[100005],b[100005]={0},n; 13 scanf("%d",&n); 14 int sum=0,minn=1000000000; 15 16 for(int i=0;i<n;i++){ 17 scanf("%d",&a[i]); 18 19 } 20 int maxx=a[0]; 21 int change=0; 22 for(int i=0;i<n;i++){ 23 if(change<maxx-a[i]){ 24 change=maxx-a[i]; 25 } 26 if(maxx<a[i]){ 27 maxx=a[i]; 28 } 29 }//maxx应该随着从左到右序列关系的变化而变化,以保证maxx和a[i]的距离正确 30 int i=0; 31 if(change<1){ 32 printf("0\n"); 33 }else{ 34 int s=log2(change)+1; 35 printf("%d\n",s); 36 } 37 38 39 } 40 }
题意:n个人,数组中n个元素是这n个人拥有的财富,然后你可以从中挑选出部分人收集起来他们的所有财富,然后给这部分人平分,财富>=x是富人,问通过这种操作,最多多少个富人
思路:其实你就可以把他想象成劫富济贫hhh,削去富人多的部分,补齐穷人少的,x就是标准线,不过是应该救济离着富人标准近的穷人才能保证富人最多
wa掉的/应该注意:1.就是注意最多限制了,先救济财富离x近的穷人,这就要求序列排序以后,循环要从后往前进行
代码:
1 #include<iostream> 2 #include<algorithm> 3 #include<cstdio> 4 #include<cmath> 5 using namespace std; 6 int main(){ 7 int t; 8 scanf("%d",&t); 9 while(t--){ 10 long long int sum=0,num=0; 11 int n,x; 12 int a[100005]; 13 scanf("%d %d",&n,&x); 14 for(int i=0;i<n;i++){ 15 scanf("%d",&a[i]); 16 if(a[i]>=x){ 17 sum+=a[i]-x; 18 num++; 19 } 20 } 21 sort(a,a+n); 22 for(int i=n-1;i>=0;i--){ 23 if(a[i]<x){ 24 if(x-a[i]<=sum){ 25 sum-=(x-a[i]); 26 num++; 27 }else{ 28 break; 29 } 30 } 31 if(sum<=0){ 32 break; 33 } 34 35 } 36 printf("%lld\n",num); 37 } 38 }
题意:炸弹爆炸,如果只是靠枪打需要花费a[i]秒,并且爆炸后对下一个炸弹的影响是b[i],也就是下一个炸弹的激发时间变成a[i+1]-b[i],并且这些炸弹按圆环排列,即第一个炸弹挨着第二个和第n个
思路:因为一个炸弹爆炸只能影响下一个炸弹特定值,所以是存在上一个引爆以后,要使下一个爆发的最小值,理想思考的话,每一个都存在上一个影响导致自身有降低触发秒数的可能,所以我们假设都存在,先不考虑是哪个触发,所以我们还是应该再存入一个数组c[i]存入a[i]-b[i-1],并计算总数sum(必须触发的),然后遍历c[i],如果c[i]大于0,则说明上一个影响后自身还是需要一定秒数,那么总的秒数就是sum-c[i]+a[i],而如果小于0,就说明,上一个爆炸直接导致这一个爆炸,那么总的秒数就是sum+a[i],找到最小的就行输出。
wa掉的/注意的:1.少用cin,cout;2.那个最后遍历c[i]找最小结果时,那个做标记的数最少记为1e18;3.并且触发点需要分情况,因为sum是理想触发自身引燃,所必须的秒数,c[i]的正负影响c[i]是不是在sum中
代码:
1 #include<iostream> 2 #include<algorithm> 3 #include<cstdio> 4 #include<cmath> 5 #define maxx 300005 6 using namespace std; 7 long long int a[maxx],b[maxx],c[maxx]; 8 long long int sum=0; 9 int main(){ 10 int t; 11 scanf("%d",&t); 12 while(t--){ 13 int n; 14 scanf("%d",&n); 15 sum=0; 16 for(int i=0;i<n;i++){ 17 scanf("%lld",&a[i]); 18 scanf("%lld",&b[i]); 19 } 20 for(int i=0;i<n;i++){ 21 if(i==0){ 22 c[i]=a[0]-b[n-1]; 23 }else{ 24 c[i]=a[i]-b[i-1]; 25 } 26 if(c[i]>0){ 27 sum+=c[i]; 28 } 29 } 30 long long int s=1000000000000000000;//数开小了 31 for(int i=0;i<n;i++){//找触发点需要分情况 32 if(c[i]>0){ 33 s=min(s,sum-c[i]+a[i]); 34 }else{ 35 s=min(s,sum+a[i]); 36 } 37 } 38 printf("%lld\n",s); 39 } 40 }
题意:两个数组a[i],b[i],并且a[i]只有-1,0,1三个元素,a[i]的前面元素可以加到后面,即A2=A1+A2,但是A1=A2+A1就不对,问数组a是否可以通过这种操作变成数组b
思路:统计数组a中-1和1的个数,然后从右往左遍历(题目的加法要求限制),如果这个元素a[i]是1,那么1的个数先减一,再判断数组b[i]与a[i]的大小,如果a[i]大,则判断前i-1个数中-1个数大小,如果存在就下一个,如果没有就不可以完成
wa掉的/注意的:1.一定看清数组a只有三种元素;2.一定是从右往左,这样才能判断,需要的数前面到底有没有,因为题目要求只能是前面加到后面;3.一定是先判断a中的元素是1还是-1还是0,因为自身不可以加到自身上,只能前i-1个可以加到第i个元素上
代码:
1 #include<iostream> 2 #include<algorithm> 3 #include<cstdio> 4 #include<cmath> 5 #define maxx 300005 6 using namespace std; 7 long long int a[maxx],b[maxx],c[maxx]; 8 long long int sum=0; 9 int main(){ 10 int t; 11 scanf("%d",&t); 12 while(t--){ 13 int n; 14 scanf("%d",&n); 15 int a[100005],b[100005]; 16 int z=0,f=0; 17 for(int i=0;i<n;i++){ 18 scanf("%d",&a[i]); 19 if(a[i]==1){ 20 z++; 21 } 22 if(a[i]==-1){ 23 f++; 24 } 25 } 26 for(int i=0;i<n;i++){ 27 scanf("%d",&b[i]); 28 } 29 int flag=0; 30 for(int i=n-1;i>=0;i--){ 31 if(a[i]==1){ 32 z--; 33 } 34 if(a[i]==-1){ 35 f--; 36 } 37 if(a[i]<b[i]){ 38 if(z>0){ 39 continue; 40 }else{ 41 flag=1; 42 break; 43 } 44 } 45 if(a[i]>b[i]){ 46 if(f>0){ 47 continue; 48 }else{ 49 flag=1; 50 break; 51 } 52 } 53 } 54 if(flag==1){ 55 printf("NO\n"); 56 }else{ 57 printf("YES\n"); 58 } 59 } 60 }
题意:对于这个题先给出定义:子数列:为原数列在开头删去0~n个数,结尾删去0~n个数后得到的数列;好数列:数列里没有和为0的子数列的数列称为好数列。题意就是给一个数列,问里面包含多少好数列。
思路:a1 a2 a3 a4 a5 b1 b2 b3 a6 ,其中b1~b3为和为0的序列,当第一重循环走到a6的位置时(a6为右端点),左端点只可能是b2 b3…a7,即起码是从某个和为0的子序列的左端点后一个数开始。事实上可能有很多和为0的子序列(可能交叉可能包含…),只需要从里面找到下标最大的一个左端点(不然的话一定会把和为0的序列包含进来),也就是说通过map[sum前缀和]=i,来判断前面是否存储过这个数值,如果存储过,那么肯定存在合为0的数组,那么组要找出最大的那个坐标(只有最大的坐标才能排除包含或者交叉的那种数组存在,可以自己找个数组遍历遍历试试),因为此时找到map[suma6]=map[suma5],所以map[suma5]对应的i都会和数组b1,b2,b3整体结和,那么i个数组为0,并且还有一个b1,b2,b2,a6,总共是遍历出以后是i+1;
wa掉的/注意的:1.好数列注意是题目样例中子序列的子序列中没有和为0的这就很难为人了;2.这个需要分开想,找到0以后,要进行前面后面分开,这样能更好的排除包含,交叉的为0数组,就是理解好和为0数组右结和(就是山前面元素的),不是一蹴而就的,应该是每遍历一次才会加1个
代码:
1 #include<iostream> 2 #include<cstdio> 3 #include<algorithm> 4 #include<cstring> 5 #include<cmath> 6 #include<map> 7 using namespace std; 8 9 #define inf 0x3f3f3f3f 10 #define ll long long 11 #define mod 1000000007 12 #define maxn 1000005 13 ll f[maxn]; 14 void init(){ 15 f[0] = 1; 16 for (int i=1; i<=maxn; ++i) f[i] = f[i-1] * i % mod; 17 } 18 ll fastpow(ll x,ll y){ 19 ll ans=1; 20 ll res=x; 21 while(y){ 22 if(y&1)ans=(ans*res)%mod; 23 res=(res*res)%mod; 24 y>>=1; 25 } 26 return ans; 27 } 28 ll lucas(ll n,ll k){ 29 ll ans=1; 30 while(n&&k){ 31 ll nn=n%mod; 32 ll kk=k%mod; 33 if(nn<kk)return 0; 34 ans=ans*f[nn]*fastpow(f[kk]*f[nn-kk]%mod,mod-2)%mod; 35 n/=mod;k/=mod; 36 } 37 return ans; 38 } 39 int main(){ 40 int n; 41 int a[200005]={0}; 42 map<long long int,long long int>mp; 43 mp[0]=0; 44 scanf("%d",&n); 45 int flag=0; 46 long long int sum=-1;//因为57行必须初始设定sum=-1 47 for(int i=1;i<=n;i++){ 48 scanf("%d",&a[i]); 49 } 50 long long int item=0,sum2=0; 51 for(int i=1;i<=n;i++){ 52 item+=a[i]; 53 if(mp.find(item)!=mp.end()){ 54 sum=max(sum,mp[item]); 55 } 56 mp[item]=i; 57 sum2+=(i-(sum+1)); 58 } 59 60 printf("%lld\n",sum2); 61 62 }
总结:挺绝望的,不过对于cf即使一直掉我觉得这东西确实上瘾,值得训练,思维是慢慢攒出来的,不是一蹴而就的,对得起每一个题,总结好理解好每一个题奇奇怪怪的思维方法慢慢就好了,已经慢慢找到了做数学题得感觉不过这周看的题太多了,该好好整整专业课了,也谢谢那些师哥的帮助!,即使给我讲的F我没懂哈哈哈,不过最近确实自己看题挺瞎的

浙公网安备 33010602011771号