Task 1:小奇采药

【问题描述】

 小奇是只天资聪颖的喵,他的梦想是成为世界上最伟⼤的医师。

 为此,他想拜喵星球最有威望的医师为师。

 医师为了判断他的资质,给他出了⼀个难题。

 医师把他带到⼀个到处都是草药的⼭洞里对他说:“小奇,这个⼭洞里有⼀些不同的草药,采每⼀株都需要⼀些时间,每⼀株也有它自身的价值。

 我会给你⼀段时间,在这段时间里,你可以采到⼀些草药。

 如果你是⼀只聪明的喵,你应该可以让采到的草药的总价值最⼤。”

【输入格式】

  第1 ⾏包括1 个整数T,表示数据组数。

 对于每组数据,第1 ⾏包括2 个整数,\(n\),\(m\),表示草药的数目和能用于采药的时间。

 接下来 \(n\) ⾏,每⾏两个整数 \(ti, vi\) 。

 保证\(m,ti,vi\) 在限制范围内均匀随机⽣成。

【输出格式】

 输出T ⾏,每⾏1 个数字,表示每组数据答案。

【样例输入】

herb.in herb.out
1 3
3 70
71 100
69 1
1 2

【数据规模与约定】

  • 对于30% 数据,\(1 <= n <= 20,1 <= m,vi,ti <= 10^4\)
  • 对于60% 数据,\(1 <= n <= 100, 1 <= m,vi,ti <= 10^5\)
  • 对于100% 数据,\(1 <= T <= 10,1 <= n <= 150,1 <= m,vi,ti<=10^9\)

 这个题目实际上是对NOIP普及组2015D1T1-洛谷【P1048】采药的改编。

 对于60%的数据很显然就是一个简单的0/1背包问题,直接推出递推式:

​ $$ f[ j ] = f[ j - v[ i ] ]+w[ i ] $$

 关键就是对于100%的数据,直接DP时间空间都不允许,10^9的范围下,\(O(nm)\)的算法无能为力。

 那么怎么处理呢?对于暴力可以DP,正解数据范围在\(10^9\)范围的题目,最常见的操作是搞一个矩阵出来。但是仔细观察会发现,很显然这个题不是用来搞矩阵的啊QWQ,根本构造不了好伐?

 注意到n和m范围差距悬殊之后,我们就意识到,这个题目可能可以搜索跑过去。考场上虽然想到了,但是一方面不擅长剪枝,另一方面总感觉复杂度不对,所以也就没敢乱搞。

 然而遗憾的是正解就是搜索+剪枝......这个题对剪枝的技巧要求还是比较高的。在写出来这个题以后,我感觉又回到了写【小木棍-数据加强版】的时候QWQ

  • 大块在前小块在后,先选大块再选小块,先排序再讲
  • 如果当前选用的体积情况已经不足以再添加哪怕最小的物品,就退出
  • 如果当前获得价值加上后面所有物品的价值也达不到原答案,就退出
  • 如果后面每一个物品都可以选上,就全选走人。(防止无效的抉择)

 然后就过了。。就过了。。过了。。。。

 搜索的复杂度果然是\(O(玄学)\)QWQ

 Talk is easy,show me the code.

  1. #include<cstdio>
  2. #include<cstring>
  3. #include<iostream>
  4. #include<algorithm>
  5. #define MAXN 160
  6. #define int long long
  7. using namespace std;
  8. int T,n,m,ans,sumv[MAXN],sumw[MAXN];
  9. struct OBJ{
  10. int v,w;
  11. bool operator<(const OBJ &rhs)const{
  12. return v>rhs.v;
  13. }
  14. }obj[MAXN];
  15. void dfs(int pos,int use,int val){
  16. ans=max(ans,val);
  17. if(pos>n)return;//at the edge
  18. if(use+obj[n].v>m)return; //cannot choose more;
  19. if(val+sumw[pos]<=ans)return;//if val is not enough
  20. if(use+sumv[pos]<=m){
  21. ans=max(ans,val+sumw[pos]);
  22. return;//one for all
  23. }//if all can use
  24. if(use+obj[pos].v<=m){
  25. dfs(pos+1,use+obj[pos].v,val+obj[pos].w);
  26. }//can choose
  27. dfs(pos+1,use,val);
  28. }
  29. signed main(){
  30. freopen("herb.in","r",stdin);
  31. freopen("herb.out","w",stdout);
  32. scanf("%lld",&T);
  33. while(T--){
  34. ans=0;
  35. scanf("%lld%lld",&n,&m);
  36. for(register int i=1;i<=n;++i){
  37. scanf("%lld%lld",&obj[i].v,&obj[i].w);
  38. }
  39. sort(obj+1,obj+1+n);
  40. for(register int i=n;i>=1;--i){
  41. sumv[i]=sumv[i+1]+obj[i].v;
  42. sumw[i]=sumw[i+1]+obj[i].w;
  43. }
  44. dfs(1,0,0);
  45. printf("%lld\n",ans);
  46. }
  47. return 0;
  48. }

Task 2:小奇的数列

【题目背景】

 小奇总在数学课上思考奇怪的问题。

【问题描述】

 给定⼀个长度为n 的数列,小奇定义,若⼀个区间\(,,[L,R](1 <= L,R <= n)\)满⾜:

 存在⼀个\(k(L <= k <= R)\), 使得对于任意的\(i(L <= i <= R)\),\(ai\) 能被\(ak\) 整除

 则称样的区间为可约的,其价值为\(R <= L\)。

 小奇想知道数列中所有可约区间的最⼤价值\(x\),以及价值为\(x\) 的可约区间个数\(num\),以及它们的左端点。

【输入格式】

 输⼊⽂件名为\(sequence.in\)

 第1 ⾏1 个整数\(n\)。

 第2 ⾏n 个整数,代表\(ai\)

【输出格式】

 输出⽂件名为\(sequence.out\)

 第1 ⾏2 个整数,\(num\) 和\(x\)。

 第2 ⾏\(num\) 个整数,从小到⼤输出所有价值为x 的区间的左端点L。

sequence.in sequence.out
5 1 3
4 6 9 3 6 2
sequence.in sequence.out
5 5 0
2 3 5 7 11 1 2 3 4 5

【数据规模与约定】

  • 对于30% 的数据,\(1 <= n <= 30; 1 <= ai <= 32\)
  • 对于60% 的数据,\(1 <= n <= 3000; 1 <=ai <= 2^{10}\)
  • 对于80% 的数据,\(1 <= n <= 300000; 1 <= ai <= 2^{20}\)
  • 对于100% 的数据,\(1 <= n <= 500000; 1 <= ai < 2^{31}\)

 暴力思路并不难想,直接枚举所有情况即可,有n3到n2几种不同的暴力。

 想要想到正解需要明白一个关键问题:

gcd和最小值类似,具有传递性。

 明白这一点,就会很容易想到可以用st表维护区间最小值和区间gcd,只要二者相等,该区间就符合条件。

 同时我们这里进行一个二分答案的优化,使复杂度优秀到可以随便水过这个题。

 数据量较大,建议写快读,复杂度O(nlogn)。

 思维难度并不大,但是区间求解gcd的方法确实很重要。

  1. #include<cmath>
  2. #include<cstdio>
  3. #include<cstring>
  4. #include<iostream>
  5. #include<algorithm>
  6. #define MAXN 500010
  7. using namespace std;
  8. int n,arr[MAXN],st_min[MAXN][35],st_gcd[MAXN][35];
  9. inline int read(){
  10. int s=0;
  11. char ch=getchar();
  12. while('9'<ch||ch<'0'){
  13. ch=getchar();
  14. }
  15. while('0'<=ch&&ch<='9'){
  16. s=s*10+ch-'0';
  17. ch=getchar();
  18. }
  19. return s;
  20. }
  21. inline int min(int x,int y){
  22. return x<y?x:y;
  23. }
  24. int gcd(int x,int y){
  25. return y?gcd(y,x%y):x;
  26. }
  27. inline bool can_use(int x){
  28. //长度为x的区间有可用的吗?
  29. int maxn=log2(x);
  30. int maxl=n-(1<<maxn)+1;
  31. for(register int i=1;i<=maxl;++i){
  32. int _min=min(st_min[i][maxn],st_min[i+x-(1<<maxn)][maxn]);
  33. int _gcd=gcd(st_gcd[i][maxn],st_gcd[i+x-(1<<maxn)][maxn]);
  34. if(_min==_gcd){
  35. // printf("len %d is ok\n",x);
  36. // printf("sequence is [%d,%d] gcd=%d min=%d \n",i,i+x-1,_gcd,_min);
  37. return true;
  38. }
  39. }
  40. return false;
  41. }
  42. inline int get_ans(int x){
  43. //长度为x的区间有可用的吗?
  44. int cnt=0;
  45. int maxn=log2(x);
  46. int maxl=n-(1<<maxn)+1;
  47. for(register int i=1;i<=maxl;++i){
  48. int _min=min(st_min[i][maxn],st_min[i+x-(1<<maxn)][maxn]);
  49. int _gcd=gcd(st_gcd[i][maxn],st_gcd[i+x-(1<<maxn)][maxn]);
  50. if(_min==_gcd){
  51. // printf("len %d is ok\n",x);
  52. // printf("sequence is [%d,%d] gcd=%d min=%d \n",i,i+x-1,_gcd,_min);
  53. cnt++;
  54. }
  55. }
  56. return cnt;
  57. }
  58. inline void output(int x){
  59. //长度为x的区间有可用的吗?
  60. int maxn=log2(x);
  61. int maxl=n-(1<<maxn)+1;
  62. for(register int i=1;i<=maxl;++i){
  63. int _min=min(st_min[i][maxn],st_min[i+x-(1<<maxn)][maxn]);
  64. int _gcd=gcd(st_gcd[i][maxn],st_gcd[i+x-(1<<maxn)][maxn]);
  65. if(_min==_gcd){
  66. // printf("len %d is ok\n",x);
  67. // printf("sequence is [%d,%d] gcd=%d min=%d \n",i,i+x-1,_gcd,_min);
  68. printf("%d ",i);
  69. }
  70. }
  71. printf("\n");
  72. }
  73. int main(){
  74. freopen("sequence.in","r",stdin);
  75. freopen("sequence.out","w",stdout);
  76. n=read();
  77. for(register int i=1;i<=n;++i){
  78. arr[i]=read();
  79. st_min[i][0]=arr[i];
  80. st_gcd[i][0]=arr[i];
  81. }
  82. int maxn=log2(n);
  83. for(register int i=1;i<=maxn;++i){
  84. int maxl=n-(1<<i)+1;
  85. for(register int j=1;j<=maxl;++j){
  86. st_min[j][i]=min(st_min[j][i-1],st_min[j+(1<<(i-1))][i-1]);
  87. st_gcd[j][i]=gcd(st_gcd[j][i-1],st_gcd[j+(1<<(i-1))][i-1]);
  88. //区间最小值和区间gcd都具有传递性,可以用st表,线段树维护
  89. }
  90. }
  91. //st表预处理区间最小值和区间gcd
  92. int l=0,r=n;
  93. while(r>l+1){
  94. int mid=(l+r)>>1;
  95. if(can_use(mid)){
  96. l=mid;
  97. }else{
  98. r=mid-1;
  99. }
  100. }
  101. int ans_len=can_use(r)?r-1:l-1;
  102. int ans_num=get_ans(ans_len+1);
  103. printf("%d %d\n",ans_num,ans_len);
  104. output(ans_len+1);
  105. return 0;
  106. // printf("ans=%d\n",ans_len);
  107. }

Task 3:切蛋糕

【问题描述】

 小奇买了⼀块⽣日蛋糕,这是⼀块矩形蛋糕,它由\(N * M\) 个小蛋糕组成,每个蛋糕的美味指数为\(T[ i ][ j ]\)。

 为了把蛋糕分给众⼈,小奇决定横着切\(A-1\) ⼑,再把得到的A 块各竖着切\(B-1\) ⼑,分成\(B\)块,这样⼀共有\(A - B\) 块。为了使⼤家都⾼兴,他希望让美味指数之和最少的那个蛋糕的美味指数最⼤。请你告诉他这个值吧。注意,你不能把小蛋糕切碎。

【输入格式】

 输⼊第⼀⾏四个数\(,,,N,M,A,B\)

 接下来\(N\)⾏,每⾏\(M\)个整数。

【输出格式】

 输出⼀⾏,表示最小值的最⼤值。

champion.in champion.out
5 4 4 2 3
1 2 2 1
3 1 1 1
2 0 1 3
1 1 1 1
1 1 1 1

【数据规模与约定】

  • 对于30% 的数据,有\(A=B=2\)
  • 对于60% 的数据,有\(A=2\)
  • 对于100% 的数据,有\(,1 <= N,M <= 500; 0 <= T[ i ][ j ] <= 4000; 1 <= A <= N; 1 <= B <= M\)。

 最小值最大,很显然二分最小值。

 考虑二分如何判断该值能否满足条件:

  • 先考虑一维的玩法:直接一个一个往前贪,如果可以就累加分割次数,分割次数够了true。
  • 二维同理,先控制行的变量,搞出来可以产生大于等于此答案的最小的行距。
  • 累加行维度上的分割次数,次数足够就true。
  • 注意维护二维前缀和。

 难度一般。code

  1. #include<cstdio>
  2. #include<cstring>
  3. #include<iostream>
  4. #include<algorithm>
  5. #define MAXN 510
  6. using namespace std;
  7. int n,m,a,b,s,mp[MAXN][MAXN],sum[MAXN][MAXN];
  8. inline bool can_use(int k){
  9. //值x是否可用?
  10. // printf("div_num=%d\n",k);
  11. int x_bg=0,x_cnt=0;//行列的始末
  12. for(register int i=1;i<=n;++i){
  13. int y_bg=0,y_cnt=0;
  14. for(register int j=1;j<=m;++j){
  15. int val=sum[i][j]-sum[i][y_bg]-sum[x_bg][j]+sum[x_bg][y_bg];
  16. //当前方块的大小
  17. if(val>=k){
  18. // printf("div in [%d,%d]\n",i,j);
  19. y_bg=j;
  20. y_cnt++;
  21. //计数,划分
  22. }
  23. }
  24. if(y_cnt>=b){
  25. // printf("y is dived. div in [%d,%d]]\n",x_bg,i);
  26. x_bg=i;
  27. x_cnt++;
  28. }
  29. }
  30. // printf("k=%d x_d/iv=%d\n",k,x_cnt);
  31. if(x_cnt>=a){
  32. // puts("is ok\n");
  33. return true;
  34. }else{
  35. // puts("No\n");
  36. return false;
  37. }
  38. }
  39. int main(){
  40. freopen("champion.in","r",stdin);
  41. freopen("champion.out","w",stdout);
  42. scanf("%d%d%d%d",&n,&m,&a,&b);
  43. for(register int i=1;i<=n;++i){
  44. for(register int j=1;j<=m;++j){
  45. scanf("%d",&mp[i][j]);
  46. sum[i][j]=mp[i][j]+sum[i-1][j]+sum[i][j-1]-sum[i-1][j-1];
  47. }
  48. }
  49. // for(register int i=1;i<=n;++i){
  50. // for(register int j=1;j<=m;++j){
  51. // printf("%2d ",sum[i][j]);
  52. // }
  53. // printf("\n");
  54. // }
  55. int l=0,r=sum[n][m];
  56. while(r>l+1){
  57. int mid=(l+r)>>1;
  58. // printf("l=%d r=%d \n",l,r);
  59. if(can_use(mid)){
  60. l=mid;
  61. }else{
  62. r=mid-1;
  63. }
  64. }
  65. int ans=can_use(r)?r:l;
  66. printf("%d ",ans);
  67. return 0;
  68. }

【清北学堂2018-刷题冲刺】Contest 7的更多相关文章

  1. 2017 清北济南考前刷题Day 7 afternoon

    期望得分:100+100+30=230 实际得分:100+100+30=230 1. 三向城 题目描述 三向城是一个巨大的城市,之所以叫这个名字,是因为城市中遍布着数不尽的三岔路口.(来自取名力为0的 ...

  2. 2017 清北济南考前刷题Day 1 afternoon

    期望得分:80+30+70=180 实际得分:10+30+70=110 T1 水题(water) Time Limit:1000ms   Memory Limit:128MB 题目描述 LYK出了道水 ...

  3. 2017 清北济南考前刷题Day 3 morning

    实际得分:100+0+0=100 T1 右上角是必败态,然后推下去 发现同行全是必胜态或全是必败态,不同行必胜必败交叉 列同行 所以n,m 只要有一个是偶数,先手必胜 #include<cstd ...

  4. 2017 清北济南考前刷题Day 3 afternoon

    期望得分:100+40+100=240 实际得分:100+40+100=240 将每个联通块的贡献乘起来就是答案 如果一个联通块的边数>点数 ,那么无解 如果边数=点数,那么贡献是 2 如果边数 ...

  5. 2017 清北济南考前刷题Day 4 afternoon

    期望得分:30+50+30=110 实际得分:40+0+0=40 并查集合并再次写炸... 模拟更相减损术的过程 更相减损术,差一定比被减数小,当被减数=减数时,停止 对于同一个减数来说,会被减 第1 ...

  6. 2017 清北济南考前刷题Day 7 morning

    期望得分:100+50+20=170 实际得分:10+50+20=80 1. 纸牌 题目描述 在桌面上放着n张纸牌,每张纸牌有两面,每面都写着一个非负整数.你的邪王真眼可以看到所有牌朝上的一面和朝下的 ...

  7. 2017 清北济南考前刷题Day 6 afternoon

    期望得分:100+100+30=230 实际得分: 正解: 枚举最高的位,这一位m是1但实际用了0 然后剩余的低位肯定是 正数就用1,负数用0 考场思路:数位DP #include<cstdio ...

  8. 2017 清北济南考前刷题Day 6 morning

    T1 贪心 10 元先找5元 20元 先找10+5,再找3张5 #include<cstdio> using namespace std; int m5,m10,m20; int main ...

  9. 2017 清北济南考前刷题Day 5 afternoon

    期望得分:100+100+30=230 实际得分:0+0+0=30 T1 直接模拟 #include<cstdio> #include<iostream> using name ...

  10. 2017 清北济南考前刷题Day 5 morning

    期望得分:100+100+0=200 实际得分: 坐标的每一位不是0就是1,所以答案就是 C(n,k) #include<cstdio> #include<iostream> ...

随机推荐

  1. 洛谷 p1219 八皇后

    刚参加完蓝桥杯 弱鸡错了好几道..回头一看确实不难 写起来还是挺慢的 于是开始了刷题的道路 蓝桥杯又名搜索杯 暴力杯...于是先从dfs刷起 八皇后是很经典的dfs问题 洛谷的这道题是这样的 上面的布 ...

  2. Ubuntu18.04安装RabbitMQ

    Ubuntu18.04安装RabbitMQ 2018年06月10日 19:32:38 dmfrm 阅读数:2492    版权声明:本文为博主原创文章,未经博主允许不得转载. https://blog ...

  3. ubuntun与qt下载地址

    http://mirrors.melbourne.co.uk/ubuntu-releases/ http://download.qt.io/archive/qt/5.4/5.4.0/ 使用u盘安装ub ...

  4. Luogu5245 【模板】多项式快速幂(多项式exp)

    A(x)k=eklnA(x).泰勒展开之后容易发现k并非在指数上,所以对p取模. #include<iostream> #include<cstdio> #include< ...

  5. Colored Sticks POJ - 2513 并查集+欧拉通路+字典树hash

    题意:给出很多很多很多很多个棒子 左右各有颜色(给出的是单词) 相同颜色的可以接在一起,问是否存在一种 方法可以使得所以棒子连在一起 思路:就是一个判欧拉通路的题目,欧拉通路存在:没奇度顶点   或者 ...

  6. 「洛谷P1516」 青蛙的约会

    洛谷题号:P1516 出处:? 主要算法:数论 难度:4.4 思路分析: 典型的同余方程.由于是纬线,绕一圈是可以绕回来的,所以是可以取模的. 阅读题目,很容易得到同余方程$ x + tm ≡ y + ...

  7. 【XSY1545】直径 虚树 DP

    题目大意 ​ 给你一棵\(n\)个点的树,另外还有\(m\)棵树,第\(i\)棵树与原树的以\(r_i\)为根的子树形态相同.这\(m\)棵树之间也有连边,组成一颗大树.求这棵大树的直径长度. \(n ...

  8. 【HDU - 5790 】Prefix(主席树+Trie树)

    BUPT2017 wintertraining(15) #7C 题意 求[min((Z+L)%N,(Z+R)%N)+1,max((Z+L)%N,(Z+R)%N)+1]中不同前缀的个数,Z是上次询问的结 ...

  9. Android热点回顾第一期

    本文由 ImportNew - 陈强 翻译自 androidweekly.如需转载本文,请先参见文章末尾处的转载要求. Importnew注:欢迎Android爱好者参与翻译文中提及的教程,请私信联系 ...

  10. 【BZOJ1299】巧克力棒(博弈论,线性基)

    [BZOJ1299]巧克力棒(博弈论,线性基) 题面 BZOJ 题解 \(Nim\)博弈的变形形式. 显然,如果我们不考虑拿巧克力棒出来的话,这就是一个裸的\(Nim\)博弈. 但是现在可以加入巧克力 ...