Codeforces#562 (DIV2) 1169
关于第一种能力的培养,有两种方法:一种是理解某些知识的建立过程:比如你能自己利用向量的乘积推出高中所有的三角函数公式的时候,没有什么和三角有关的题能难住你了;还有一种是靠大量训练提高熟练度,在头脑里建立固定映射:看到一个题不是想到定理,是想到自己做过的类似的题,靠回忆已知的解法来解题。
第二种能力的培养就很简单了,计算能力纯靠练习,论证能力嘛,我非常建议诸君看一看数学家们是如何从自然数出发去构造实数模型的,搞清其中的逻辑关系,我想,大部分时候你也可以论证得很好了。
作者:匿名用户
链接:https://www.zhihu.com/question/22594591/answer/21921862
来源:知乎
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
(1): 一般我们碰到的前面的题都是水题,按照题意模拟即可,如果发现这道题有点困难或者麻烦,那不用想了,题目读错了,肯定是读题的问题。
(2): 一般我们都是碰到哪几种题呢?
个人认为有三种 1:直接读完题就发现会做了或者思维很通畅,说明这是道水题或者做过很多遍这样的模型 ,{不要得意,这只是说明这道题见过很多遍了,接下里已经没有太多必要训练这种题或者这道题的必要了}
2: 猜想(直觉)觉得某种做法是对的,然后对着这这种做法验证一下,发现是对的 这种东西要求具备的东西是 见多识广(即见过差不多的题目) 或者 是碰到过类似的情况 怎么练的话 还是只能多刷题了。
3:我们发现一道题我们一开始根本就没有太多想法,不知道怎么做(那么很高兴,这道题的训练价值很高) {{{ 这时候就会放飞思维了,在瞎几把乱想,怎么把瞎几把乱想的这个点掐死是非常重要的,因为这样是在瞎猫去撞死耗子,能撞上就ok,不能撞上就GG}}} 这个其实也没有办法,我们唯一做的也只是说确定好思考的方向,围绕这个点的性质去想,去联想以前做过的题目,看能不能弄出来
4:这时候就得分成两种方向来思考(但是紧跟大局,就是数据范围,先跑通样例,跟着样例跑,根据样例在一些已经有的性质里面推广或者假设一些性质应用来做,总之一切跟着性质来做。。。。。。。。)
A题:题意:有俩个地铁,一个从 1 → 2 → …→ n → 1→ 2 →…, 一个 从 n → n-1 →…→ 1 → n → n-1→ …。地铁同时开并且地铁经过一个站的时间都相同,俩个人一个坐前面那俩,一个坐后面那俩,给你他们的起点站和终点站,问他们在这过程中有没有可能同时在一个站,可能就输出YES,否则输出NO。
(1):因为n很小,100步内肯定能到达终点,所以我们只需暴力即可,这个得认真细致的观察啊???????
(2):水题,下面是代码:
1 #include <cstdio> 2 #include <cstring> 3 #include <iostream> 4 #include <algorithm> 5 #include <cmath> 6 #include <bitset> 7 typedef long long ll; 8 using namespace std; 9 int n,a,x,b,y; 10 int main(){ 11 scanf("%d%d%d%d%d",&n,&a,&x,&b,&y); 12 int flag=0; 13 int st1=a,st2=b; 14 while(1){ 15 if(st1!=x){ 16 if (st1 < n) st1++; 17 else { 18 st1 = 1; 19 } 20 } 21 if(st2!=y){ 22 if (st2 > 1) st2--; 23 else { 24 st2 = n; 25 } 26 } 27 if(st1==st2){flag=1;break;} 28 29 if(st1==x) break; 30 if(st2==y) break; 31 } 32 if(flag) printf("YES\n"); 33 else printf("NO\n"); 34 return 0; 35 }
B题: 给出m对不大于n的数,判断是否可以找出两个不相等的数,使这两个数可以出现在任意对中。
(1): 首先我们应该知道的一个点就是不要读错题!!!!!!我一直读成了区间,区间的话难度就会加大很多很多了?
(2): 第二遍才开始读对题,这个读题是个关键活啊?
(3): 我们不一定要把重心都放在两个数上面(即一定要枚举出这两个数),可以把他看到怎么确定任意对都包含这个两个数中的任意一个或者两个上面
(4): 那么这样就好像多了,我们可以知道的是第一个就肯定是第一个中一个,假设如果第二个也是的话,我们可以验证一下,如果接下来的运算中他们不能找到一个和这两个圆环中完全不等的环
(5): 那么直接输出就可,如果找到一个完全不等的数,那么我们分4种情况讨论即可,这个是关键之笔,也是神来之手,下面是代码
1 #include <cstring> 2 #include <algorithm> 3 #include <cmath> 4 #include <bitset> 5 #include <iostream> 6 typedef long long ll; 7 using namespace std; 8 const int maxn=301000; 9 int n,m; 10 int xi[maxn],yi[maxn]; 11 int s1=0,s2=0,s3=0,s4=0; 12 int main(){ 13 scanf("%d%d",&n,&m); 14 for(int i=1;i<=m;i++){ 15 scanf("%d%d",&xi[i],&yi[i]); 16 if(xi[i]>yi[i]) swap(xi[i],yi[i]); 17 } 18 int flag=0; 19 for(int i=2;i<=m;i++){ 20 if((xi[i]==xi[1])||(yi[i]==yi[1])||(xi[i]==yi[1])||(yi[i]==xi[1])) continue; 21 else{ 22 flag=i; 23 } 24 } 25 if(!flag){printf("YES\n");return 0;} 26 for(int i=1;i<=m;i++){ 27 if((xi[1]==xi[i]||xi[1]==yi[i])||(yi[flag]==xi[i]||yi[flag]==yi[i])){ 28 s1++; 29 } 30 } 31 32 for(int i=1;i<=m;i++){ 33 if((xi[1]==xi[i]||xi[1]==yi[i])||(xi[flag]==xi[i]||xi[flag]==yi[i])){ 34 s2++; 35 } 36 } 37 38 for(int i=1;i<=m;i++){ 39 if((yi[1]==xi[i]||yi[1]==yi[i])||(xi[flag]==xi[i]||xi[flag]==yi[i])){ 40 s3++; 41 } 42 } 43 44 for(int i=1;i<=m;i++){ 45 if((yi[1]==xi[i]||yi[1]==yi[i])||(yi[flag]==xi[i]||yi[flag]==yi[i])){ 46 s4++; 47 } 48 } 49 50 if(s1==m||s2==m||s3==m||s4==m) printf("YES\n"); 51 else printf("NO\n"); 52 return 0; 53 }
下面还有一个更妙的用桶的方法: 来自Codeforces这一场的第一名
1 #include <iostream> 2 #include <cstdio> 3 #include <cstring> 4 #include <algorithm> 5 #include <cmath> 6 #include <bitset> 7 using namespace std; 8 int l[300009], r[300009], cnt[300009], n, m; 9 bool test(int x) { 10 memset(cnt, 0, sizeof cnt); 11 int cntx = 0; 12 for(int i = 0; i < m; ++i) 13 if(x == l[i] or x == r[i]) 14 ++cntx; 15 else 16 cnt[l[i]]++, cnt[r[i]]++; 17 for(int i = 0; i <= n; ++i) 18 if(cntx + cnt[i] == m) 19 return 1; 20 return 0; 21 } 22 //一种更妙的用桶的方法 23 24 int main(){ 25 cin >> n >> m; 26 for(int i = 0; i < m; ++i) 27 scanf("%d%d", &l[i], &r[i]); 28 if(test(l[0]) or test(r[0])) 29 cout << "YES\n"; 30 else 31 cout << "NO\n"; 32 return 0; 33 }
C题:有n个数,每次可以选择k个,将他们+1并对m取模。问最少进行多少次操作,使得序列是非递减的。
感觉这个k由于是个序列,只要最高修改次数是s就可,其他小于等于也行,这个肯定是后面的越高越好,前面的越低越好,然后直接二分mid,从后往前贪心即可,感觉是个简单套路题啊
1 #include <cstdio> 2 #include <cstring> 3 #include <iostream> 4 #include <algorithm> 5 #include <cmath> 6 #include <bitset> 7 #include <vector> 8 #include <set> 9 typedef long long ll; 10 using namespace std; 11 const int maxn=300100; 12 int n,m; 13 int num[maxn],f[maxn]; 14 15 bool check(int mid){ 16 for(int i=n;i>=1;i--){ 17 if(i==n){f[i]=min(m-1,num[i]+mid);} 18 else{ 19 if(num[i]<=f[i+1]) f[i]=min(f[i+1],num[i]+mid); 20 else{ 21 // num[i] > f[i+1] 22 if((num[i]+mid)<=(m-1)) return false; 23 f[i]=min((num[i]+mid)-(m),f[i+1]); 24 } 25 } 26 } 27 return true; 28 } 29 30 int main(){ 31 scanf("%d%d",&n,&m); 32 for(int i=1;i<=n;i++) scanf("%d",&num[i]); 33 int lb=0,rb=3*m+2,ans=0; 34 while(lb<=rb){ 35 int mid=(lb+rb)/2; 36 if(check(mid)){ 37 ans=mid; 38 rb=mid-1; 39 }else lb=mid+1; 40 } 41 printf("%d\n",ans); 42 return 0; 43 }
D题:给定一个01串,判断这个串中有几个区间可以满足 s[i]=s[i+k]=s[i+2k] ,输出这样的区间数的全部数量。
那么对于这个问题我们该怎么来做呢?
(1): 首先我们得确定一个不重不漏算的方案,能保证我们在不重不漏的情况下,能完整的算出所有的区间的数量
(2): 那么我们可以确定的一个东西是我们可以通过枚举左边界,通过固定左边界,我们就可以做到不充不漏,那么我们接下来怎么确定右边界呢?????
(3): 我们应当通过性质来确定右边界,而不是什么性质都还没有,然后就套用以前做过的题来直接看能不能降到log(n)啥的, 这个思维方向好像是搞错了的?????????
(4): 这个性质怎么发现,无他,只能借助手动玩来搞出来
(5): 我们先假设当前这一个是1,然后想想最坏的结果是什么,肯定是不能让你111直接连起来,肯定要搞破坏 ,得 1221221 这样搞破坏,但是无论怎么搞破坏,都会发现一点肯定不会超过9个值,所以我们可以发现这个的右边界
的确定可以很容易的确定
(6): 然后这道题其实就可以A掉了
下面是代码:
1 /* 2 i --> i+k --> i+2*k 3 4 l r 5 */ 6 #include <cstring> 7 #include <cmath> 8 #include <bitset> 9 #include <algorithm> 10 #include <iostream> 11 #include <cmath> 12 #include <bitset> 13 typedef long long ll; 14 using namespace std; 15 const int maxn=301000; 16 int len; 17 char s[maxn]; 18 ll ans=0; 19 int main(){ 20 scanf("%s",s+1); 21 len=(int)strlen(s+1); 22 for(int i=1;i<=len;i++){ 23 int tmp=0; 24 for(int j=i+2;j<=min(len,i+8);j++){ 25 // i,j; 26 int ok=0; 27 for(int k=1;k<=len;k++){ 28 if(j-2*k<i) break; 29 if(s[j]==s[j-k]&&s[j-k]==s[j-2*k]){ 30 ok=1; 31 } 32 } 33 if(ok==1){ ans+=(len-j+1);break;} 34 } 35 } 36 printf("%lld\n",ans); 37 return 0; 38 }
E题:题意:给你一个长度为n的序列,有q次询问,每次询问给出两个位置x和y(x < y),问是否可从x到达y?可达的定义是:如果存在一个序列(假设长度为k),其中p1 = x, pk = y,并且这个序列中a[pi] & a[p(i + 1)] != 0。
(1):讲道理,这道题还是挺妙的,日常的想法是这肯定是一个n^3的floyd,但是他太大了,根本就做不到n^3的时间内解决,我们得想其他办法
(2):我们考虑怎么才能到达yi呢?????
(3):我们考虑位运算的性质,我们只能通过一个与yi相近的有同样二进制位的数开始转移,从这个数转移过去,我们才能弄到yi上面,那这样的数我们怎么才能到
(4):我们可以这样搞,弄一个数组保存当前这个数的这个二进制能到达的最近的那个二进制位的数保存下来
(5): 然后我们只要枚举yi的二进制位,对每个yi有的二进制位,我们看dp[xi][i]是不是小于yi 就可以了
(6): 接下来是代码:
1 #include <cstdio> 2 #include <cstring> 3 #include <algorithm> 4 #include <iostream> 5 #include <cmath> 6 #include <bitset> 7 typedef long long ll; 8 using namespace std; 9 const int maxn=300100; 10 int dp[maxn][22]; 11 int n,q; 12 int num[maxn],las[25]; 13 int main(){ 14 scanf("%d%d",&n,&q); 15 for(int i=1;i<=n;i++) scanf("%d",&num[i]); 16 for(int i=0;i<=20;i++) las[i]=n+1; 17 for(int i=0;i<=20;i++) dp[n+1][i]=n+1; 18 for(int i=n;i>=1;i--){ 19 for(int j=0;j<=20;j++){ 20 dp[i][j]=n+1; 21 } 22 for(int j=0;j<=20;j++){ 23 if((num[i]>>j)&1){ 24 dp[i][j]=i; 25 for(int k=0;k<=20;k++) dp[i][k]=min(dp[i][k],dp[las[j]][k]); 26 las[j]=i; 27 } 28 } 29 } 30 while(q--){ 31 int xi,yi; 32 int flag=0; 33 scanf("%d%d",&xi,&yi); 34 for(int i=0;i<=20;i++){ 35 if((num[yi]>>i)&1){ 36 if(dp[xi][i]<=yi){ 37 flag=1; 38 } 39 } 40 } 41 if(flag) printf("Shi\n"); 42 else printf("Fou\n"); 43 } 44 return 0; 45 }