[关闭]
@happyforever 2018-10-29T14:18:58.000000Z 字数 9656 阅读 552

10.27上午解题报告

清北学堂 解题报告


各题状况

期望得分:
实际得分:

T1

推了半小时的因式分解,发现不会,又感觉暴力的复杂度没啥问题,就敲了个暴力扔那不管了
精度问题。。。不在考虑范围内
(虽然我交了两份代码一个是小数点后位一个是小数点后位)

T2

总共写了2小时,期间零零碎碎大概用了半小时写注释(毕竟自己记性差没法怪别人。。。)
最后居然写了行还是第四第五种状况不想讨论了的结果

然后就因为忘记讨论模数和分子分母的情况成功挂成暴力分
气到炸裂

T3

最后剩下分钟的时候想到这题是线段树
然后就。。。放弃

题目及考场代码

T1

图片.png

  1. /*
  2. * ...
  3. * 推了半小时的公式转化
  4. * 然后TM。。。放弃了
  5. * 毕竟后面几个题都是数学题
  6. * 这个题。。。写个暴力滚粗
  7. * 反正N<=100
  8. * 精度误差。。。不在考虑范围内
  9. * (屡教不改.jpg)
  10. */
  11. #include <cstdio>
  12. #include <cmath>
  13. const int N=101;
  14. const long double e=2.7182818;
  15. long double s[N],emp,pk;
  16. int n,k;
  17. int main()
  18. {
  19. freopen("r.in","r",stdin);
  20. freopen("r.out","w",stdout);
  21. scanf("%d%d",&n,&k);
  22. for(int i=1;i<=n;++i)scanf("%Lf",&s[i]);
  23. for(int i=1;i<=n;++i)emp+=pow(e,s[i]);
  24. pk=pow(e,s[k])/emp;
  25. printf("%.5Lf",log(emp)-pk);
  26. fclose(stdin);fclose(stdout);
  27. return 0;
  28. }

T2

图片.png

  1. /*
  2. for(int i=1;i<=n;++i)
  3. for(int j=0;j<=k;++j)
  4. ans+=(n/i)*pow(i,j)%mod;
  5. 以上是直接通过题面得出的模拟代码,循环的嵌套顺序不影响结果
  6. for(int j=0;j<=k;++j)
  7. for(int i=1;i<=n;++i)
  8. ans+=(n/i)*pow(i,j)%mod;
  9. 显然当k=0的时候,就是求个整除分块
  10. for(int l=1,r;l<=n;l=r+1)
  11. {
  12. r=n/(n/l);
  13. ans+=(r-l+1)*(n/l);
  14. }
  15. 对于n/l,有r-l+1次贡献
  16. 而推广到k=1,考虑到乘法分配律
  17. 对于连续的一块,(n/l)*(1+l)+(n/l)*(1+l+1)+...+(n/l)*(1+r)
  18. 也就是(n/l)*((1+l)+(1+l+1)+(1+l+2)+...+(1+r))
  19. 后面的式子可以变成((r-l+1)+r*(r+1)/2-(l-1)*l/2)
  20. 整理:(n/l)*((r-l+1)+(r*(r+1)/2-(l-1)*l/2))
  21. 再推广到k=2,考虑到乘法分配律和平方和公式。。。
  22. (n/l)*(1+l+l*l)+(n/l)*(1+l+1+(l+1)*(l+1))+...+(n/l)*(1+r+r*r)
  23. 平方和公式:1*1+2*2+...+n*n=n*(n+1)(2*n+1)/6
  24. 那么也就是((r-l+1)+(r*(r+1)/2-(l-1)*l/2)+(r*(r+1)(2*r+1)/6-(l-1)*l*(2*(l-1)+1)/6)
  25. 整理:(n/l)*((r-l+1)+(r*(r+1)/2-(l-1)*l/2)+(r*(r+1)(2*r+1)/6-(l-1)*l*(2*l-1)/6)
  26. 然后是立方和公式。。。。
  27. 1^3+2^3+...+n^3=(n*n*(n+1)*(n+1))/4
  28. 4次方和公式
  29. 1^4+2^4+...+n^4=n*(n+1)*(2*n+1)*(3*n^2+3*n-1)/30
  30. 10:50调出来
  31. 爽翻
  32. 有点害怕会爆掉。
  33. */
  34. #include <cstdio>
  35. long long n,ans,emp,l,r,x,sum;
  36. int m,k;
  37. const int mod=1e9+7;
  38. int main()
  39. {
  40. freopen("q.in","r",stdin);
  41. freopen("q.out","w",stdout);
  42. int T;scanf("%d",&T);
  43. while(T--)
  44. {
  45. ans=0;
  46. scanf("%lld%d%d",&n,&m,&k);
  47. // m=1e9+7;
  48. // printf("%lld %d %d\n",n,m,k);
  49. for(l=1;l<=n;l=r+1)
  50. {
  51. x=n/l,r=n/x;x%=m;
  52. // ans+=(r-l+1)*(n/l);
  53. sum=(r-l+1);
  54. // printf("%lld ",sum);
  55. if(k<1)goto E;
  56. sum=(sum+r*(r+1)/2-(l-1)*l/2)%m;
  57. if(k<2)goto E;
  58. sum=(sum+r*(r+1)*(2*r+1)/6-(l-1)*l*(2*l-1)/6)%m;
  59. if(k<3)goto E;
  60. sum=(sum+((r*r*(r+1)*(r+1)/4)%m-((l-1)*(l-1)*l*l/4)%m))%m;
  61. if(k<4)goto E;
  62. sum=(sum+r*(r+1)*(2*r+1)*(3*r*r+3*r-1)/30-(l-1)*l*(2*l-1)*(3*(l-1)*(l-1)+3*(l-1)-1)/30)%m;
  63. E: emp=x*sum%m;
  64. ans=(ans+emp)%m;
  65. // printf("%lld\n",sum);
  66. }
  67. printf("%lld\n",ans);
  68. }
  69. fclose(stdin),fclose(stdout);
  70. return 0;
  71. }

T3

图片.png

  1. /*
  2. * 线段树?
  3. * 还剩30分钟,自闭了
  4. * 耶
  5. */
  6. #include <cstdio>
  7. inline int read()
  8. {
  9. int n=0,w=1;register char c=getchar();
  10. while(c<'0' || c>'9'){if(c=='-')w=-1;c=getchar();}
  11. while(c>='0' && c<='9')n=n*10+c-'0',c=getchar();
  12. return n*w;
  13. }
  14. struct Segment_Tree{
  15. };
  16. int main()
  17. {
  18. freopen("y.in","r",stdin);
  19. freopen("y.out","w",stdout);
  20. printf("26\n16");
  21. fclose(stdin);fclose(stdout);
  22. return 0;
  23. }

正解及代码

T1

暴力算就行了

随便做,送分题

这里给出正解的思路:

cmath库中有三个函数,pow(x,y),log(x),exp(x),第一个是求,第二个是求,第三个是求

三个函数按照题目模拟求出以及即可

  1. #include <cstdio>
  2. #include <cmath>
  3. const int N=101;
  4. const long double e=2.7182818;
  5. long double s[N],emp,pk;
  6. int n,k;
  7. int main()
  8. {
  9. freopen("r.in","r",stdin);
  10. freopen("r.out","w",stdout);
  11. scanf("%d%d",&n,&k);
  12. for(int i=1;i<=n;++i)scanf("%Lf",&s[i]);
  13. for(int i=1;i<=n;++i)emp+=pow(e,s[i]);
  14. pk=pow(e,s[k])/emp;
  15. printf("%.5Lf",log(emp)-pk);
  16. fclose(stdin);fclose(stdout);
  17. return 0;
  18. }

T2

给出的公式是这个样子


考虑当的时候是一个经典的问题:整除分块

这里仅仅简单提一下这个东西:

考虑会在中出现次,所以会对对答案有的贡献

图片.png

假设,那么for的时候直接算出来然后迭代器直接从跳到就行了

以上就是整除分块的内容。。。

代码非常好实现,看我考场T2注释就行了

考虑对公式的转化:

对于分的相同的每一块考虑计算

假设,那么就有

展开变成:


对于后面的四个sigma,这样写看起来不是很方便,我们可以拆开

其他同理

也就是说,我们需要平方和公式,立方和公式以及四次方和公式

**baidu**可以知道

在清北的考试是不断网的。
就算平方和的背过了,那考场上应该怎么推立方和、四次方和的公式呢?

已知有这样一个公式

也就是说方和的公式是一个有个未知数的多项式方程

所以我们考虑写出来个不同的多项式方程然后高斯消元求解

考场上可以这么暴力地求,毕竟手推公式不是所有人都能做到

但是还有一件要考虑的事情。。。

长者本来是想开

也就是说,在上面的某个过程会爆long long

一个显然的想法是在各种乘法都加一个

但是很不幸不一定是质数并且不具有同除性

回顾上面几个公式

可以发现,前三个是一定可以把分子分母约掉的(一定有一个是偶数,里面一定有一个是的倍数,不能保证的因子)

所以前三个公式可以通过讨论将分母先处理掉然后再

而对于第四个公式,可以考虑对模数进行某种操作使得之后依然能够整除

这时一个给出了一个思路:把模数乘以

这样确实是可以的,因为这样操作之后我们可以保证之后的值至少对这几个质因子

那么本题的细节就愉快地讨论完了

  1. #include <cstdio>
  2. long long n,ans,emp,l,r,x,sum;
  3. int m,k;
  4. const int mod=1e9+7;
  5. long long calc1(long long x)
  6. {
  7. long long y=x+1;
  8. if(x&1)y>>=1;
  9. else x>>=1;
  10. return x*y;
  11. }
  12. long long calc2(long long x)
  13. {
  14. long long y=x+1,z=2*x+1;
  15. if(x%3==0)x/=3;
  16. else
  17. {
  18. if(y%3==0)y/=3;
  19. else z/=3;
  20. }
  21. if(x&1)y>>=1;
  22. else x>>=1;
  23. return x*y*z;
  24. }
  25. int main()
  26. {
  27. freopen("q.in","r",stdin);
  28. freopen("q.out","w",stdout);
  29. int T;scanf("%d",&T);
  30. while(T--)
  31. {
  32. ans=0;
  33. scanf("%lld%d%d",&n,&m,&k);
  34. // m=1e9+7;
  35. // printf("%lld %d %d\n",n,m,k);
  36. for(l=1;l<=n;l=r+1)
  37. {
  38. x=n/l,r=n/x;x%=m;
  39. // ans+=(r-l+1)*(n/l);
  40. sum=(r-l+1);
  41. // printf("%lld ",sum);
  42. if(k<1)goto E;
  43. sum=(sum+calc1(r)%m-calc1(l-1)%m+m)%m;
  44. if(k<2)goto E;
  45. sum=(sum+calc2(r)%m-calc2(l-1)%m+m)%m;
  46. if(k<3)goto E;
  47. sum=(sum+(calc1(r)%m*calc1(r)%m-calc1(l-1)%m*calc1(l-1)%m)+m)%m;
  48. if(k<4)goto E;
  49. m*=30;
  50. sum=(sum+calc2(r)%m*(3*r*r+3*r-1)%m/30-calc2(l)%m*(3*(l-1)*(l-1)+3*(l-1)-1)%m/30+m)%m;
  51. E: emp=x*sum%m;
  52. ans=(ans+emp)%m;
  53. // printf("%lld\n",sum);
  54. }
  55. printf("%lld\n",ans);
  56. }
  57. fclose(stdin),fclose(stdout);
  58. return 0;
  59. }

T3

对于操作二、三可以看做是线段树维护区间加一个等差数列以及求区间和
换句话说,可以把每次操作看做往一段区间上面扔这样的线段
图片.png
假设每次放上去的等差数列看成线段,那么图上表现得更高的线段会覆盖低的线段(高低反映相对大小)
那么操作四就是对每个点维护已经覆盖过当前点的最高值是多少
考虑可能出现三种更新情况
图片.png

图片.png
这两种情况可以直接对覆盖的区间全部选择丢掉小的,留下大的
图片.png
但是对于上面这种情况就无法全部舍弃或者保留了
考虑像线段树的下传一样分区间进行操作,考虑对于分出来的区间能否直接判断

  1. #include<cstdio>
  2. #include<cstdlib>
  3. #include<cstring>
  4. #include<algorithm>
  5. #include<cctype>
  6. using namespace std;
  7. const int BUF_SIZE = 30;
  8. char buf[BUF_SIZE], *buf_s = buf, *buf_t = buf + 1;
  9. #define PTR_NEXT() \
  10. { \
  11. buf_s ++; \
  12. if (buf_s == buf_t) \
  13. { \
  14. buf_s = buf; \
  15. buf_t = buf + fread(buf, 1, BUF_SIZE, stdin); \
  16. } \
  17. }
  18. #define readint(_n_) \
  19. { \
  20. while (*buf_s != '-' && !isdigit(*buf_s)) \
  21. PTR_NEXT(); \
  22. bool register _nega_ = false; \
  23. if (*buf_s == '-') \
  24. { \
  25. _nega_ = true; \
  26. PTR_NEXT(); \
  27. } \
  28. int register _x_ = 0; \
  29. while (isdigit(*buf_s)) \
  30. { \
  31. _x_ = _x_ * 10 + *buf_s - '0'; \
  32. PTR_NEXT(); \
  33. } \
  34. if (_nega_) \
  35. _x_ = -_x_; \
  36. (_n_) = (_x_); \
  37. }
  38. #define root 1,n,1
  39. #define lson l,m,rt<<1
  40. #define rson m+1,r,rt<<1|1
  41. const int maxn=100010;
  42. const int mo=1000000007;
  43. int n,m;
  44. struct node
  45. {
  46. int l,r;
  47. int v1;
  48. int col1_a,col1_b;
  49. int v2;
  50. int col2_a,col2_b;
  51. node(){
  52. v1=v2=0;
  53. col1_a=col1_b=0;
  54. col2_a=col2_b=0;
  55. }
  56. }z[maxn<<2|1];
  57. void build(int l,int r,int rt)
  58. {
  59. z[rt].l=l;z[rt].r=r;
  60. if (l==r) return;
  61. int m=(l+r)>>1;
  62. build(lson);
  63. build(rson);
  64. }
  65. void modify1(int l,int r,int rt,int nowl,int nowr,int A,int B);
  66. void col1(int rt,int A,int B)
  67. {
  68. if (z[rt].col1_a || z[rt].col1_b)
  69. {
  70. int a1=A,b1=B;
  71. int a2=z[rt].col1_a,b2=z[rt].col1_b;
  72. if (a1<a2) swap(a1,a2),swap(b1,b2);
  73. int s=z[rt].r-z[rt].l;
  74. int r1=a1+b1*s;
  75. int r2=a2+b2*s;
  76. if (r1>=r2)
  77. {
  78. z[rt].col1_a=a1;
  79. z[rt].col1_b=b1;
  80. }
  81. else
  82. {
  83. int p=(a1-a2)/(b2-b1);
  84. while (a1+b1*p>=a2+b2*p)
  85. p++;
  86. //printf("%d %d %d\n",z[rt].l,z[rt].r,p);
  87. modify1(z[rt].l,z[rt].r,rt,z[rt].l,z[rt].l+p-1,a1,b1);
  88. modify1(z[rt].l,z[rt].r,rt,z[rt].l+p,z[rt].r,a2+p*b2,b2);
  89. }
  90. }
  91. else
  92. {
  93. z[rt].col1_a=A;
  94. z[rt].col1_b=B;
  95. }
  96. }
  97. void col2(int rt,int A,int B)
  98. {
  99. int s = z[rt].r-z[rt].l+1;
  100. z[rt].v2 = (z[rt].v2 + (long long)s*A + (long long)s*(s-1)/2%mo*B)%mo;
  101. z[rt].col2_a += A;
  102. if (z[rt].col2_a>=mo) z[rt].col2_a -= mo;
  103. z[rt].col2_b += B;
  104. if (z[rt].col2_b>=mo) z[rt].col2_b -= mo;
  105. }
  106. void push_col(int rt)
  107. {
  108. if (z[rt].col2_a || z[rt].col2_b)
  109. {
  110. int m=(z[rt].l+z[rt].r)>>1;
  111. int A=z[rt].col2_a;
  112. int B=z[rt].col2_b;
  113. col2(rt<<1,A,B);
  114. col2(rt<<1|1,(A+(long long)B*(m+1-z[rt].l))%mo,B);
  115. z[rt].col2_a=0;
  116. z[rt].col2_b=0;
  117. }
  118. }
  119. void modify1(int l,int r,int rt,int nowl,int nowr,int A,int B)
  120. {
  121. if (nowl<=l && r<=nowr)
  122. {
  123. col1(rt,(l-nowl)*B+A,B);
  124. return;
  125. }
  126. int m=(l+r)>>1;
  127. if (nowl<=m) modify1(lson,nowl,nowr,A,B);
  128. if (m<nowr) modify1(rson,nowl,nowr,A,B);
  129. }
  130. void modify2(int l,int r,int rt,int nowl,int nowr,int A,int B)
  131. {
  132. if (nowl<=l && r<=nowr)
  133. {
  134. col2(rt,((l-nowl)*B+A)%mo,B);
  135. return;
  136. }
  137. push_col(rt);
  138. int m=(l+r)>>1;
  139. if (nowl<=m) modify2(lson,nowl,nowr,A,B);
  140. if (m<nowr) modify2(rson,nowl,nowr,A,B);
  141. z[rt].v2=z[rt<<1].v2+z[rt<<1|1].v2;
  142. if (z[rt].v2>=mo) z[rt].v2-=mo;
  143. }
  144. int query2(int l,int r,int rt,int p,int nowv=0)
  145. {
  146. nowv=max(nowv,(p-z[rt].l)*z[rt].col1_b+z[rt].col1_a);
  147. if (l==r) return (nowv+z[rt].v2)%mo;
  148. push_col(rt);
  149. int m=(l+r)>>1;
  150. if (p<=m) return query2(lson,p,nowv);
  151. else return query2(rson,p,nowv);
  152. }
  153. int query1(int l,int r,int rt,int nowl,int nowr)
  154. {
  155. if (nowl<=l && r<=nowr) return z[rt].v2;
  156. push_col(rt);
  157. int m=(l+r)>>1;
  158. int ans=0;
  159. if (nowl<=m) ans=query1(lson,nowl,nowr);
  160. if (m<nowr) ans+=query1(rson,nowl,nowr);
  161. if (ans>=mo) ans-=mo;
  162. return ans;
  163. }
  164. int main()
  165. {
  166. readint(n);
  167. readint(m);
  168. build(root);
  169. for (int a=1;a<=m;a++)
  170. {
  171. //printf("%d\n",a);
  172. int opt;
  173. readint(opt);
  174. if (opt==1)
  175. {
  176. int l,r,A,B;
  177. readint(l);
  178. readint(r);
  179. readint(A);
  180. readint(B);
  181. modify1(root,l,r,A,B);
  182. }
  183. if (opt==2)
  184. {
  185. int l,r,A,B;
  186. readint(l);
  187. readint(r);
  188. readint(A);
  189. readint(B);
  190. modify2(root,l,r,A,B);
  191. }
  192. if (opt==3)
  193. {
  194. int l,r;
  195. readint(l);
  196. readint(r);
  197. printf("%d\n",query1(root,l,r));
  198. }
  199. if (opt==4)
  200. {
  201. int p;
  202. readint(p);
  203. printf("%d\n",query2(root,p));
  204. }
  205. }
  206. return 0;
  207. }

讲烂了的一些东西

  1. Dev自动补全库,本地AC提交CE

    1. 可以先把平时常用的库全写上(反正编译时间不算在运行时间内),你写五十行的include也不会有人管你
    2. bits/stdc++.h尽量不要用,因为NOIP一直没有明确说明能不能用
    3. 放弃Dev,或者至少在最后要脱离Dev编译一次代码(注意如果机器上其他地方有MinGW就不要用Dev里面的)
  2. 几个变量名,是雷区

    1. x1,x2,y1,y2,nextalgorithm(cmath)库里面已经定义过了的
    2. 解决方法:大写或在所有用到的变量名后面都加个下划线或随机字符(对象的名字或自己的名字)
  3. long double

    1. 读入时应当声明一个double类型,scanf("%lf",&);然后把这个读入的值赋给long double的变量;输出时则是先把long double赋给一个double然后printf("%lf",);

      这样根本不会有什么问题因为输入输出的时候不会对原值做任何操作导致爆double,该爆只会在中间操作的时候爆

    2. cmath里面的powdouble类型的,exp也是;想用long double的函数一般可以在后面加个,比如powl,expl之类

  4. 暴力写对啊。。。。提升代码能力

添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注