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 }
View Code

 

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 }
View Code

 

C - Middle Class

题意: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 }
View Code

 

D - Circle of Monsters

题意:炸弹爆炸,如果只是靠枪打需要花费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 }
View Code

 

E - Kind Anton

题意:两个数组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 }
View Code

 

F - Eugene and an array

题意:对于这个题先给出定义:子数列:为原数列在开头删去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 }
View Code

 

总结:挺绝望的,不过对于cf即使一直掉我觉得这东西确实上瘾,值得训练,思维是慢慢攒出来的,不是一蹴而就的,对得起每一个题,总结好理解好每一个题奇奇怪怪的思维方法慢慢就好了,已经慢慢找到了做数学题得感觉不过这周看的题太多了,该好好整整专业课了,也谢谢那些师哥的帮助!,即使给我讲的F我没懂哈哈哈,不过最近确实自己看题挺瞎的

 

posted @ 2020-05-30 00:40  bonel  阅读(167)  评论(0)    收藏  举报