[关闭]
@dxbdly 2020-12-20T07:00:27.000000Z 字数 6140 阅读 232

C2020 2020.10.2模拟赛

信息学——模拟赛


考试情况

考场上期望得分VS实际得分

期望得分:

T1 T2 T3 总分
100 70 100 270

实际得分:

T1 T2 T3 总分
100 55 100 255

T1 物品选取

分析

考虑DP,不难想到对于A,B,C类分别处理(同一状态,不同方程)。

首先B和C的类型是很好处理的,两个基本的背包。

考虑如何处理A(似乎有人弄出理论的算法?反正我不会)。

考虑枚举2个量,总体积,当前物品取的体积。

总复杂度理论,似乎最坏情况过不了,但实测是过了的(论常数的重要性)。

出题人给的正解也是这个……

  1. #include<iostream>
  2. #include<cstdio>
  3. #include<string>
  4. using namespace std;
  5. inline int read()
  6. {
  7. int x=0;
  8. char c=getchar();
  9. bool f=0;
  10. while (!isdigit(c))
  11. f|=(c=='-'),c=getchar();
  12. while (isdigit(c))
  13. x=(x<<3)+(x<<1)+(c^48),c=getchar();
  14. return f?-x:x;
  15. }
  16. int n,m;
  17. int f[2005];
  18. inline int max(int x,int y)
  19. {
  20. return x>y?x:y;
  21. }
  22. inline void work1(int a,int b)
  23. {
  24. for (register int i=m;i>=0;--i)
  25. for (register int j=1;j<=i;++j)
  26. f[i]=max(f[i],f[i-j]+a*j*j-b*j);
  27. }
  28. inline void work2(int a,int b,int c)
  29. {
  30. for (register int i=m;i>=0;--i)
  31. for (register int j=1;j<=c;++j)
  32. if (b*j<=i)
  33. f[i]=max(f[i],f[i-b*j]+a*j);
  34. }
  35. inline void work3(int a,int b)
  36. {
  37. for (register int i=b;i<=m;++i)
  38. f[i]=max(f[i],f[i-b]+a);
  39. }
  40. int main(){
  41. // freopen("pack.in","r",stdin);
  42. // freopen("pack.out","w",stdout);
  43. n=read(),m=read();
  44. for (register int i=1;i<=n;++i)
  45. {
  46. int x=read(),a,b,c;
  47. if (x==1)
  48. a=read(),b=read(),work1(a,b);
  49. if (x==2)
  50. a=read(),b=read(),c=read(),work2(a,b,c);
  51. if (x==3)
  52. a=read(),b=read(),work3(a,b);
  53. }
  54. printf("%d",f[m]);
  55. return 0;
  56. }

思考与总结

所以说对于这种玄学的复杂度不要不敢写,毕竟理论上界也就

很多时候往往会到不了理论上界,当然常数必须优秀(再次论常数重要性)。

T2 蛇形矩阵

分析

蛇形矩阵

最难的一道,还是个鬼畜推公式的题。

首先得会求一个数对应的位置和一个位置对应的数。

那么考虑计算每个数对于当前矩阵的贡献。

显然对于坐标为,所求矩阵坐标为,数为的贡献为

将所有贡献求和,那么你就有60分了(但实测有很多常数优秀的跑到了60以上)。

考虑满分的范围是,容易联想到根号复杂度。

考虑对于为右下角的矩阵进行拆分。

会发现它可以拆成一个正方形,加上很多宽为1的矩形(后文称为条)。

而我们发现正方形可以拆成诸多7字形(如图中5->6->7->8->9),而且条显然就是7字型的一部分,算法相似。

而7字型的个数是的数量级,如果我们能用的时间得到每个7字型就能以的时间通过。

那么7字型如何求呢?

考虑拆成两个条再减去重叠部分(5->6->7与7->8->9),那么只要推条的公式就好了。

注意推的时候有多种情况要分类,需要考虑横条与竖条,以及7字型的奇偶性(算上单独的条公式总共6个……)。

下面的推导只推奇数7且横条的情况,其他同理。

接下来就开始愉快的推公式:

蛇形矩阵

设当前是第个7字,输入的对应的数为

则右上角数为,坐标为,左下角为,坐标为,右下角为右上角与左下角的平均数,坐标为


,将提出:

发现括号中每项都有一个,提出

提出,两个都可写成求和:

发现第一个系数为等差,第二个为平方和,化简:

到此为止,横条公式完成。

剩下几种情况照这种推一遍就是了,几乎没什么不同。

最后由于算了右下两次,单独减掉即可。

  1. //The code is from dawn_sdy
  2. #include<bits/stdc++.h>
  3. #define int long long
  4. using namespace std;
  5. inline int read()
  6. {
  7. int x=0;
  8. char c=getchar();
  9. bool f=0;
  10. while (!isdigit(c))
  11. f=f|(c=='-'),c=getchar();
  12. while (isdigit(c))
  13. x=(x<<3)+(x<<1)+(c^48),c=getchar();
  14. return f?-x:x;
  15. }
  16. const int mod=1e9+7;
  17. int n,m,x,y,ans;
  18. inline int get_x(int num)
  19. {
  20. int now=sqrt(num);
  21. if (now*now==num)
  22. return now&1?1:now;
  23. now++;
  24. int cnt=((now-1)*(now-1)+1+now*now)/2;
  25. if (num>=cnt)
  26. return now&1?now-(num-cnt):now;
  27. else
  28. return now&1?now:now-(cnt-num);
  29. }
  30. inline int get_y(int num)
  31. {
  32. int now=sqrt(num);
  33. if (now*now==num)
  34. return now&1?now:1;
  35. now++;
  36. int cnt=((now-1)*(now-1)+1+now*now)/2;
  37. if (num>=cnt)
  38. return now&1?now:now-(num-cnt);
  39. else
  40. return now&1?now-(cnt-num):now;
  41. }
  42. inline int calc(int i,int j,int now,int k)//我只退了4种情况公式,最后算条可以巧妙的用
  43. {
  44. if (k==1)
  45. return ((y-j+1)%mod*(i*now%mod*x%mod-i*(i-1)/2*(x+now)%mod+i*(i-1)*(2*i-1)/6%mod+mod)%mod)%mod;
  46. if (k==2)
  47. return ((x-j+1)%mod*(i*now%mod*y%mod+i*(i-1)/2*(y-now)%mod-i*(i-1)*(2*i-1)/6%mod+mod)%mod)%mod;
  48. if (k==3)
  49. return ((y-j+1)%mod*(i*now%mod*x%mod+i*(i-1)/2*(x-now)%mod-i*(i-1)*(2*i-1)/6%mod+mod)%mod)%mod;
  50. if (k==4)
  51. return ((x-j+1)%mod*(i*now%mod*y%mod-i*(i-1)/2*(y+now)%mod+i*(i-1)*(2*i-1)/6%mod+mod)%mod)%mod;
  52. }
  53. signed main(){
  54. n=read(),x=get_x(n),y=get_y(n),m=min(x,y);
  55. for (register int i=m;i>=1;--i)
  56. {
  57. int cnt=((i-1)*(i-1)+1+i*i)/2,now;
  58. if (i&1)
  59. {
  60. now=i*i%mod,ans=(ans+calc(i,i,now,1))%mod;
  61. now=(i*i-2*i+2)%mod,ans=(ans+calc(i,i,now,2))%mod;
  62. ans=(ans-cnt*(x-i+1)%mod*(y-i+1)%mod+mod)%mod;
  63. }
  64. else
  65. {
  66. now=(i*i-2*i+2)%mod,ans=(ans+calc(i,i,now,3))%mod;
  67. now=i*i%mod,ans=(ans+calc(i,i,now,4))%mod;
  68. ans=(ans-cnt*(x-i+1)%mod*(y-i+1)%mod+mod)%mod;
  69. }
  70. ans=(ans+mod)%mod;
  71. }
  72. for (register int i=m+1;i<=y;++i)
  73. {
  74. int now;
  75. if (i&1)
  76. now=i*i%mod,ans=(ans+calc(x,i,now,1))%mod;
  77. else
  78. now=(i*i-2*i+2)%mod,ans=(ans+calc(x,i,now,3))%mod;
  79. ans=(ans+mod)%mod;
  80. }
  81. for (register int i=m+1;i<=x;++i)
  82. {
  83. int now;
  84. if (i&1)
  85. now=(i*i-2*i+2)%mod,ans=(ans+calc(y,i,now,2))%mod;
  86. else
  87. now=i*i%mod,ans=(ans+calc(y,i,now,4))%mod;
  88. ans=(ans+mod)%mod;
  89. }
  90. printf("%lld",ans=(ans+mod)%mod);
  91. return 0;
  92. }

思考与总结

这种题考场上的确性价比不高,先打部分分,正解靠后做。

T3 正则表达式

分析

这题最重要的话是这句:

  1. 如果存在AB的连接的同时也存在BA的连接的话,那么AB实际上处于同一局域网内,可以通过本地传输,这样花费的传输时间为0

翻译一下就是:

  1. 如果A,B可以相互到达,则其边权为0

相互到达,这是很明显的强连通分量的特点。

考虑跑遍缩点,则同一个中的点之间的边权都为,重新建图跑遍最短路就好了。

总结一下就是:建图——>缩点——>重新建图——>最短路

  1. //The code is from dawn_sdy
  2. #include<bits/stdc++.h>
  3. #define INF INT_MAX
  4. #define mp make_pair
  5. using namespace std;
  6. inline int read()
  7. {
  8. int x=0;
  9. char c=getchar();
  10. bool f=0;
  11. while (!isdigit(c))
  12. f|=(c=='-'),c=getchar();
  13. while (isdigit(c))
  14. x=(x<<3)+(x<<1)+(c^48),c=getchar();
  15. return f?-x:x;
  16. }
  17. int n,m;
  18. struct node{
  19. int v,nex;
  20. int w;
  21. }line[2000005],edge[2000005];
  22. int head[1000005],len,tot,num;
  23. int dfn[200005],low[200005],col[200005],st[200005],top;
  24. priority_queue< pair<int,int> > q;
  25. int dist[200005];
  26. bool vst[200005];
  27. inline void make_map(int u,int v,int w)
  28. {
  29. len++;
  30. edge[len].nex=head[u];
  31. edge[len].v=v;
  32. edge[len].w=w;
  33. head[u]=len;
  34. }
  35. inline void Tarjan(int x)
  36. {
  37. dfn[x]=low[x]=++num,st[++top]=x;
  38. for (register int i=head[x];i;i=edge[i].nex)
  39. {
  40. int y=edge[i].v;
  41. if (!dfn[y])
  42. Tarjan(y),low[x]=min(low[x],low[y]);
  43. else
  44. if (!col[y])
  45. low[x]=min(low[x],dfn[y]);
  46. }
  47. if (dfn[x]==low[x])
  48. {
  49. col[x]=++tot;
  50. while (top&&st[top]!=x)
  51. col[st[top--]]=tot;
  52. top--;
  53. }
  54. }
  55. inline void Dijstra()
  56. {
  57. memset(vst,0,sizeof(vst));
  58. for (register int i=1;i<=n;++i)
  59. dist[i]=INF;
  60. q.push(mp(0,1)),dist[1]=0;
  61. while (!q.empty())
  62. {
  63. int x=q.top().second;
  64. q.pop();
  65. if (vst[x])
  66. continue;
  67. vst[x]=1;
  68. for (register int i=head[x];i;i=edge[i].nex)
  69. {
  70. int y=edge[i].v,z=edge[i].w;
  71. if (dist[y]>dist[x]+z)
  72. dist[y]=dist[x]+z,q.push(mp(-dist[y],y));
  73. }
  74. }
  75. }
  76. int main(){
  77. // freopen("regexp.in","r",stdin);
  78. // freopen("regexp.out","w",stdout);
  79. n=read(),m=read();
  80. for (register int i=1;i<=m;++i)
  81. {
  82. int u=read(),v=read(),w=read();
  83. make_map(u,v,w);
  84. }
  85. for (register int i=1;i<=n;++i)
  86. if (!dfn[i])
  87. Tarjan(i);
  88. for (register int x=1;x<=n;++x)
  89. for (register int i=head[x];i;i=edge[i].nex)
  90. {
  91. int y=edge[i].v;
  92. if (col[x]==col[y])
  93. edge[i].w=0;
  94. }
  95. Dijstra();
  96. printf("%d",dist[n]);
  97. return 0;
  98. }

思考与总结

缩点是重要的中间步骤处理,很多时候都需要用缩点创造条件,一定不能把忘了。

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