清北学堂Day2

Part1:模拟题

今天的题有点难啊,只得了第一题的60分。不过让人欣慰的是,大多数人都只得了第一题的六十分。

60分做法:z老师说的是打表或者暴力就能拿到。暴力应该是要做一点优化。我是枚举了a和(a*b)然后判断b是否是因数(即在数组a中)。刚开始我想用set储存因数,因为可以直接用s.count()来判断是不是因数。但是有别的问题。就是他只能访问到第一个或最后一个数。所以要更新a的话只能删掉第一个数。这样的话后面再判断是否为因数时会受影响。比较气的是我都写完了才发现这个问题,然后又删掉老老实实的用数组写了一遍,耽误了不少时间。(不过后两题也不会写)

我的代码:

 1 #include <iostream>
 2 #include <cmath>
 3 #include <cstring>
 4 #include <cstdio>
 5 #include <cstdlib>
 6 #include <algorithm>
 7 using namespace std;
 8 int n,ans,ge;
 9 int a[10101];
10 bool is_prime(int x)  //判断质数 
11 {
12     if(x==2) return true;
13     for(int i=2;i<=sqrt(x);i++)
14         if(x%i==0) return false;
15     return true;
16 }
17 void find(int x) //找因数 
18 {
19     for(int i=1;i<=x;i++)
20         if(x%i==0) a[++ge]=i;
21 }
22 
23 int main()
24 {
25     
26     freopen("a.in","r",stdin);
27     freopen("a.out","w",stdout);
28     
29     scanf("%d",&n);
30     ans=1;
31     for(int i=2;i<=n;i++)
32     {
33         if(is_prime(i)) {ans+=3;continue;}  //即(1,1) (1,i) (i,1)三种情况 
34         memset(a,0,sizeof(a));
35         ge=0;find(i);
36         ans+=2*ge-1;  //即(1,1) (1,a[1])(a[1],1) (1,a[2])(a[2],1) ... (1,a[ge])(a[ge],1)
37         for(int j=2;j<ge;j++)//枚举a 
38         {
39             for(int k=j+1;k<=ge;k++) //枚举(a*b) 
40             {
41                 if(a[k]%a[j]!=0) continue; /*除不尽的就不要再考虑了。这点刚开始没想到,错了好几次*/ 
42                 int c=a[k]/a[j];   
43                 if(i%c==0) ans++;//如果b也在a数组中,则符合条件
44             }
45         }
46     }
47     printf("%d",ans);
48     return 0;
49 }
a60

100分做法:题意实际上就是枚举a,b找有多少数是(a*b)的倍数(将倍数设为c),即枚举a*b*c。可假定a<b<c,那么将枚举出来的数*6即为最终答案【(1,2,3)(1,3,2)(2,1,3)...共六种排列方式,答案一样】(此处埋下一个BUG)

因为a<b<c,所以a的循环只需要到,即(a=1;a*a*a<=n;a++)就行了。

同理,b*b<=n/a(a*b*c<n  b*c<n/a;因为b<c,所以b*b<n/a)

然而仅仅这样做是不对的。因为我们没有考虑其中有2个或3个数相等的情况。下面要再开个循环考虑一下。

标程:

 1 #include<cstdio>
 2 #include<cstdlib>
 3 #include<cstring>
 4 
 5 using namespace std;
 6 
 7 long long n;
 8 
 9 #ifdef unix
10 #define LL "%lld"
11 #else
12 #define LL "%I64d"
13 #endif
14 
15 int main()
16 {
17     freopen("a.in","r",stdin);
18     freopen("a.out","w",stdout);
19 
20     scanf(LL,&n);
21     long long ans=0,tmp=0;
22     for (long long a=1,v;a*a<=(v=n/a);a++,ans++)
23         for (long long b=a+1;b*b<=v;b++)
24             tmp+=n/(a*b)-b;
25     ans+=tmp*6;
26     tmp=0;
27     for (long long a=1,v;(v=a*a)<=n;a++)  //考虑两个/三个数相等的情况 
28     {
29         tmp+=n/v;
30         if (a*a<=n/a) tmp--;
31     }
32     ans+=tmp*3;
33     printf(LL "\n",ans);
34 
35     return 0;
36 }
a100

今天的题解笔记就到这里,后两题太难了弃了。

其他的一点小tips:

1 //longlong在不同系统的输出不同,以下写法可以避免在不知道测评系统的情况下导致输出错误 
2 #ifdef WIN32 
3 #define LL "%I64d"
4 #else
5 #define LL "%lld"
6 #endif
7 //-------------------------------------------------------------------------------------------------------
8 //在实数上做二分不能以L+1==R作为退出条件,不然会无限循环下去。可以直接枚举,达到一定的精度(比如30次或者50次) 

 


Part2:数据结构专题

  • 常见的数据结构:
  1. 数组、链表
  2. 队列、栈
  3. 堆(仅需知道如何用priority_queue实现)
  4. 并查集
  5. 树状数组或线段树(掌握一个即可,线段树学习资料:线段树完整版
  6. 树上LCA(最近公共祖先,用倍增)
  7. DFS序列:把一棵二维的树通过DFS搜索顺序记录为线性序列,且每棵子树的所有节点都在这个序列上的一段连续区间上:

左图的DFS序列为:1 2 4 5 3 6 7   转换为线性后,就可以用线段树解决相关问题了:)

9.括号序列:依然基于DFS 【名称来源:+为左括号,-为右括号,则和为零是是合法的括号】

还是上面那个图,它的括号序列为:+1 +2 +4 -4 +5 -5 -2 +3 +6 -6 +7 -7 +8 -8 -3 -1

                                                             -4代表4已经遍历完了

那么,它有什么用呢?

举个例子,若要找1——3——6这条链,我们在上面的这个序列中找到+1,+6,发现不属于这条链上的点全都抵消掉了。这样我们就能找到所有这条链经过的点了。

注意:括号序列有一个先决条件。那就是序列必须是自上而下的,不会转弯(比如5——2——3——1就不行,在1处转了弯)

10.启发式合并:每次把最小的并进去,这样最快是O(nlogn)的

11.分治:归并排序

12.单调队列:维护单调性。

上边这个序列显然是不单调的。

为了让它变成单调序列我们进行如下操作:若当前元素插入后会导致序列不单调,则删掉原序列的最后一个数,可重复多次,直到插入当前元素后仍为单调序列。

它的应用主要就是在滑动窗口。下面通过一道例题来体会思想:

posted @ 2017-10-02 19:58  小蒟蒻  阅读(142)  评论(0编辑  收藏  举报