清北学堂Day7

Part1--模拟题

今天是最后一天有模拟题,然而不知道为什么,我们这个区好多人的程序没有收上去(包括我)。所以至今也不知道自己多少分。不过前两题都是打的暴力(觉得自己除了暴力真的是什么都不会。。)

还是看一下题吧

(第一题)

1.计数

(count.cpp/c/pas)

时间限制:1s

内存限制:256MB

【问题描述】

    给出m个数a[1],a[2],…,a[m]

    求1~n中有多少数不是a[1],a[2],…,a[m]的倍数。

【输入】

输入文件名为count.in。

第一行,包含两个整数:n,m

第二行,包含m个数,表示a[1],a[2],…,a[m]

【输出】

输出文件名为count.out。

输出一行,包含1个整数,表示答案

【输入输出样例】

count.in

count.out

10 2

2 3

3

【数据说明】

对于60%的数据,1<=n<=106

对于另外20%的数据,m=2

对于100%的数据,1<=n<=109,0<=m<=20,1<=a[i]<=109

60分做法:纯枚举。

100分做法:容斥原理。

这个可以由‘另外20%的数据’来推出:如果m=2的话,如下图:

易得:ans=A+B-(A交B)

扩展一下,在m!=2的时候也是同理。

我的代码:

 1 #include <iostream>
 2 #include <cmath>
 3 #include <cstring>
 4 #include <cstdio>
 5 #include <cstdlib>
 6 #include <algorithm>
 7 using namespace std;
 8 bool a[1010101];
 9 int read()
10 {
11     int t=0,f=1;
12     char ch=getchar();
13     while(ch<'0' || ch>'9')
14     {
15         if(ch=='-') {f=-1;ch=getchar();}
16     }
17     while(ch>='0' && ch<='9') {t=t*10+ch-'0';ch=getchar();}
18     return t*f;
19 }
20 int main()
21 {
22     /*freopen("count.in","r",stdin);
23     freopen("count.out","w",stdout);*/
24     int n,m;
25     n=read();m=read();
26     int cnt=0;
27     for(int i=1;i<=m;i++)
28     {
29         int x;
30         x=read();
31         for(int j=x;j<=n;j+=x)
32         {
33             if(!a[j]) {a[j]=1;cnt++;}
34             //cout<<"i="<<i<<"  j="<<j<<"  cnt="<<cnt<<endl;
35         }
36     }
37     printf("%d",n-cnt);
38     system("pause");
39     return 0;
40 }
a

标程(有一半都是头文件和各种define):

 1 #include<iostream>
 2 #include<algorithm>
 3 #include<cstdio>
 4 #include<cstring>
 5 #include<cmath>
 6 #include<cstdlib>
 7 using namespace std;
 8 typedef long long ll;
 9 typedef long double ld;
10 typedef pair<int,int> pr;
11 const double pi=acos(-1);
12 #define rep(i,a,n) for(int i=a;i<=n;i++)
13 #define per(i,n,a) for(int i=n;i>=a;i--)
14 #define Rep(i,u) for(int i=head[u];i;i=Next[i])
15 #define clr(a) memset(a,0,sizeof a)
16 #define pb push_back
17 #define mp make_pair
18 #define putk() putchar(' ')
19 ld eps=1e-9;
20 ll pp=1000000007;
21 ll mo(ll a,ll pp){if(a>=0 && a<pp)return a;a%=pp;if(a<0)a+=pp;return a;}
22 ll powmod(ll a,ll b,ll pp){ll ans=1;for(;b;b>>=1,a=mo(a*a,pp))if(b&1)ans=mo(ans*a,pp);return ans;}
23 ll gcd(ll a,ll b){return (!b)?a:gcd(b,a%b);}
24 ll read(){
25     ll ans=0;
26     char last=' ',ch=getchar();
27     while(ch<'0' || ch>'9')last=ch,ch=getchar();
28     while(ch>='0' && ch<='9')ans=ans*10+ch-'0',ch=getchar();
29     if(last=='-')ans=-ans;
30     return ans;
31 }
32 void put(ll a){
33     if(a<0)putchar('-'),a=-a;
34     int top=0,q[20];
35     while(a)q[++top]=a%10,a/=10;
36     top=max(top,1);
37     while(top--)putchar('0'+q[top+1]);
38 }
39 //head
40 ll ans=0;
41 int n,m,a[25];
42 ll Gcd(ll a,ll b){
43     if(!b)return a;
44     return gcd(b,a%b);
45 }
46 void dfs(int dep,ll t,int flag){
47     if(t>n)return;
48     if(dep==m+1){
49         ans+=n/t*flag;
50         return;
51     }
52     dfs(dep+1,t,flag);
53     dfs(dep+1,t/Gcd(t,a[dep])*a[dep],-flag);
54 }
55 int main(){
56     n=read();
57     m=read();
58     rep(i,1,m)a[i]=read();
59     dfs(1,1,1);
60     cout<<ans<<endl;
61     return 0;
62 }
astd

(2)第二题

2.第k大区间

(kth.cpp/c/pas)

时间限制:1s

内存限制:256MB

【问题描述】

定义一个长度为奇数的区间的值为其所包含的的元素的中位数。

现给出n个数,求将所有长度为奇数的区间的值排序后,第K大的值为多少。

【输入】

输入文件名为kth.in。

       第一行两个数n和k

第二行,n个数。(0<=每个数<231

【输出】

输出文件名为kth.out。

一个数表示答案。

【输入输出样例】

kth.in

kth.out

4 3

3 1 2 4

2

 

【样例解释】

[l,r]表示区间l~r的值

[1,1]:3

[2,2]:1

[3,3]:2

[4,4]:4

[1,3]:2

[2,4]:2

【数据说明】

对于30%的数据,1<=n<=100;

对于60%的数据,1<=n<=300

对于80%的数据,1<=n<=1000

对于100%的数据,1<=n<=100000, k<=奇数区间的数

30%做法:枚举+排序 O(n^3logn)

60%做法:固定左端点,每次改变右端点只需加上1个点,如果新来的数比较小,就和之前的交换。   O(n^3)

*求最大值、最小值、第k大,有90%都是二分答案*

100%做法:二分答案--->转为逆序对问题(前缀和)  O(nlognlogn)

我的代码(依然是暴力。。):

 1 #include <iostream>
 2 #include <cmath>
 3 #include <cstring>
 4 #include <cstdio>
 5 #include <cstdlib>
 6 #include <algorithm>
 7 using namespace std;
 8 int a[101010],b[101010],c[101010];
 9 int read()
10 {
11     int t=0,f=1;
12     char ch=getchar();
13     while(ch<'0' || ch>'9')
14     {
15         if(ch=='-') {f=-1;ch=getchar();}
16     }
17     while(ch>='0' && ch<='9') {t=t*10+ch-'0';ch=getchar();}
18     return t*f;
19 }
20 
21 
22 int main()
23 {
24     freopen("kth.in","r",stdin);
25     freopen("kth.out","w",stdout);
26     int n,k;
27     n=read();k=read();
28     for(int i=1;i<=n;i++) a[i]=read();
29     int cnt=0,cnt1=0;
30     for(int i=1;i<=n;i++)
31     {
32         cnt=0;
33         memset(b,0,sizeof(b));
34         for(int j=i;j<=n;j+=2)
35         {
36             b[++cnt]=a[j];
37             if(j-1>i) b[++cnt]=a[j-1];
38             sort(b+1,b+cnt+1);
39             c[++cnt1]=b[(j-i+1)/2+1];
40             //cout<<"i="<<i<<"  j="<<j<<"  c[cnt1]="<<c[cnt1]<<endl;
41         }
42     }
43     sort(c+1,c+cnt1+1);
44     printf("%d",c[n-k+1]);
45     //system("pause");
46     return 0;
47 }
b

标程:

 1 #include<iostream>
 2 #include<algorithm>
 3 #include<cstdio>
 4 #include<cstring>
 5 #include<cmath>
 6 #include<cstdlib>
 7 using namespace std;
 8 typedef long long ll;
 9 typedef long double ld;
10 typedef pair<int,int> pr;
11 const double pi=acos(-1);
12 #define rep(i,a,n) for(int i=a;i<=n;i++)
13 #define per(i,n,a) for(int i=n;i>=a;i--)
14 #define Rep(i,u) for(int i=head[u];i;i=Next[i])
15 #define clr(a) memset(a,0,sizeof a)
16 #define pb push_back
17 #define mp make_pair
18 #define fi first
19 #define sc second
20 ld eps=1e-9;
21 ll pp=1000000007;
22 ll mo(ll a,ll pp){if(a>=0 && a<pp)return a;a%=pp;if(a<0)a+=pp;return a;}
23 ll powmod(ll a,ll b,ll pp){ll ans=1;for(;b;b>>=1,a=mo(a*a,pp))if(b&1)ans=mo(ans*a,pp);return ans;}
24 ll read(){
25     ll ans=0;
26     char last=' ',ch=getchar();
27     while(ch<'0' || ch>'9')last=ch,ch=getchar();
28     while(ch>='0' && ch<='9')ans=ans*10+ch-'0',ch=getchar();
29     if(last=='-')ans=-ans;
30     return ans;
31 }
32 //head
33 #define N 110000
34 int a[N],b[N],c[N],d[N],e[N],n,m;
35 ll k;
36 int lowbit(int x){
37     return x&(-x);
38 }
39 int bin(int k){
40     int l=1,r=m;
41     while(l<r){
42         int mid=(l+r)/2;
43         if(k<=d[mid])r=mid;
44         else l=mid+1;
45     }
46     return l;
47 }
48 void add(int x){
49     for(;x<=m;x+=lowbit(x))e[x]++;
50 }
51 int find(int x){
52     int ans=0;
53     for(;x;x-=lowbit(x))ans+=e[x];
54     return ans;
55 }
56 ll check(int k){
57     c[0]=0;
58     rep(i,1,n)c[i]=c[i-1]+(a[i]>=k);
59     rep(i,0,n)c[i]=c[i]*2-i,d[i+1]=c[i];
60     sort(d+1,d+n+2);
61     d[0]=1;
62     rep(i,2,n+1)
63         if(d[i]!=d[d[0]])d[++d[0]]=d[i];
64     m=d[0];
65     ll ans=0;
66     rep(i,0,n)c[i]=bin(c[i]);
67     rep(i,0,m)e[i]=0;
68     rep(i,0,n)
69         if(i&1)add(c[i]);
70         else ans+=find(c[i]-1);
71     rep(i,0,m)e[i]=0;
72     rep(i,0,n)
73         if((i&1)==0)add(c[i]);
74         else ans+=find(c[i]-1);
75     return ans;
76 }
77 int main(){
78     n=read();k=read();
79     rep(i,1,n)a[i]=read(),b[i]=a[i];
80     sort(b+1,b+n+1);
81     b[0]=1;
82     rep(i,2,n)
83         if(b[i]!=b[b[0]])b[++b[0]]=b[i];
84     int l=1,r=b[0];
85     while(l<r){  //二分 
86         int mid=(l+r)/2+1;
87         ll tt=check(b[mid]);
88         if(tt==k){
89             cout<<b[mid]<<endl;
90             return 0;
91         }
92         if(check(b[mid])>k)l=mid;
93         else r=mid-1;
94     }
95     cout<<b[l]<<endl;
96     return 0;
97 }
bstd

第三题弃疗:)


 Part2今日专题--二分+贪心(今天主要是通过一些题目来讲的算法)

  • 二分大法(为什么要学二分:不断缩小边界,直到求出答案)
  1.  poj2976:二分答案k,看有没有一种方案使其存在。二分k

  2.  poj2728:二分答案,花费总和,收益总和>=这个答案。

    总花费/总收益>=0  -->跑一遍最大生成树

  3. poj3621:找到一个环,使这个环上的边权>0

    首先把所有点加入队列(开始均为0),跑SPFA,最后的效果:如果有一个点进入SPFA 超过m次,则结束,存在负权圈(正权圈权的相反数)

  4. 第k大区间3:统计有多少区间,他的众数出现的次数>t(当前二分值).即存不存在一个数次数大于等于t

    O(n^3):枚举左右边界,扫一遍这个区间,每出现一个数i,就把它扔到s[i]这个桶里,即s[i]++;结束后可以求出最大。(桶排序)

       枚举右端点r,看有多少个左端点是合法的。左端点要满足的性质:存在单调性,2~5合法,1~5也合法。因此左端点的数量是1~i

    首先让l=0.r从1~n扫,找到上一个r[i]出现的位置,更新l,此时l~r都是合法的。以此类推。假如最后得到的l为4,那么当l为1~4时,r这个右区间都是合法的

    问题:如何找数字x在上t次出现在哪?

    以权重为第一关键字,位置为第二关键字排序,最后那个就是数字x最后出现的位置。

  5. 1. noip2015跳石头:依然是二分。二分最短距离。如果移走<m块石子,说明距离t可以增大。否则减少。

    首先把距离小<=t的删掉。这样就找到了第一个距离岸边>t的石头。接着搜。如果又遇到了一个距离<t的,肯定是删掉后面的石子(即这段小于t的距离有两个石子端点,删掉后面那个)。

    这样就能得到移除的石子数量,与m比较可以进一步二分。

  6. 1. noip2010关押罪犯:二分冲突值。用0和1表示它属于那个监狱,有冲突关系说明数字不一样(即相邻监狱)。

    一开始先不分配,随便挑一个出来编号为0,把和他矛盾的人编号为1,再把和他矛盾的矛盾的人编号为0……

当出现如图所示情况时,染色无法成功(二分图的判定)

如果不合法,减小二分的冲突值。否则增大。

  • 贪心大法
  1.  石子合并:Huffman树(解决信息编码问题)

【冷知识】:在26个字母中,字母e出现的次数是最多的

2. noi2015荷马史诗:把合并两堆变成合并k堆

3。 阿狸和桃子的游戏:边权分成两半,各加到两个端点上。为了让一个人尽量大,一个人尽量小,让他们一人拿一半,那么边权就消失了。之后从大到小排一下序,一个人拿1357,一个人拿2468就行了。。

4. Usaco2007 Nov Tanning :

如果没有下限:拿最小的那瓶防晒霜,优先满足限制最大的那个奶牛

有下限:对于最小的防晒霜,把所有能用它的奶牛都挑出来,给上限最小(即限制最多)的奶牛用。

5. 特技飞行:最刺激的放两边

贪心题好多都是猜的

6. 矩阵分割:先切代价大的那条。因此可以把权值按从大到小排序,依次切割。

7.“meet in the middle”

  • 提一下分块大法:

    分块是什么?

    分成根号n,每段根号n个数。

    分别求出每段的最小值,把最小值找出来,再求最小值。

  • 整体二分

    倍增(ST表)

    F[i][j]记录的是以i为终点,2^j这么长

 

posted @ 2017-10-07 20:17  小蒟蒻  阅读(237)  评论(0编辑  收藏  举报