当前位置:   article > 正文

2022中国大学生程序设计竞赛(CCPC)高职专场_ccpc真题

ccpc真题

一,期望

题意:

  • 初始时你有1−n这n个正整数和一个空的序列,你准备玩一个往序列中加数字的无聊游戏。
  • 游戏进行n轮,在游戏的每一轮中你要向序列尾部加入一个还未被加入的数,最终的序列将会是一个长度为n的排列。
  • 在某一轮游戏开始前,令此时所有未被加入的数的和是sum,那么这一轮加入一个未被加入的数x的概率是sumx​。
  • 求最终序列逆序对个数的期望,答案对109+7取模。
  • 对于一个序列a1​,a2​,...,an​,对于(i,j)满足,ai​>aj​且1≤i<j≤n,则称(ai​,aj​)是一对逆序对。

思路:

  1. 考虑一对逆序对(i,j)在所有排列下出现的期望。发现显然i出现在j前面,与其他数无关,所以所有排列下i出现在j前面的期望都是\frac{i}{j+i}(i>j)
  2. 所以答案就是所有逆序对的期望相加。观察出相加实际就是等差数组求和
  1. #include <bits/stdc++.h>
  2. using namespace std;
  3. #define ll long long
  4. #define endl "\n"
  5. #define int long long
  6. const int N = 3e5 + 10;
  7. const int mod=1e9+7;
  8. ll fastmi(ll base, ll power)
  9. {
  10. ll ans = 1;
  11. while (power)
  12. {
  13. if (power & 1)ans=ans*base%mod;
  14. base=base*base%mod;
  15. power >>=1;
  16. }
  17. return ans;
  18. }
  19. void mysolve()
  20. {
  21. int n;
  22. cin>>n;
  23. int ans=0;
  24. for(int i=3; i<=2*n-1; ++i)
  25. ans=(ans+((min(i-1,n)+i/2+1)*(min(i-1,n)-i/2)/2)%mod*fastmi(i,mod-2)%mod)%mod;
  26. cout<<ans<<endl;
  27. }
  28. int32_t main()
  29. {
  30. std::ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
  31. ll t=1;
  32. //cin >> t;
  33. while (t--)
  34. {
  35. mysolve();
  36. }
  37. system("pause");
  38. return 0;
  39. }

重排数组

题意

  • 给定一个长度为n的数组a1​,a2​,...,an​,定义一个数组的权值为将数按照下标顺序拼接在一起
  • 例如:一个长度为3的数组a1​=11,a2​=12,a3​=2,那么数组的权值为11122
  • 现在要求出数组a的所有排列的权值和,答案对109+7取模
  • 对于长度为n的数组有n!种排列,例如对于a1​,a2​,a3​来说,有6种排列分别是:a1​,a2​,a3​ ; a1​,a3​,a2​ ; a2​,a1​,a3​ ; a2​,a3​,a1​ ; a3​,a1​,a2​ ; a3​,a2​,a1​.

思路:

  1. ai在每个排列的贡献,取决有前面的有多少个元素
  2. 设dp[i][j]为前i个元素,处理了j个在前面产生的贡献,如果我们求出来dp[i][j]。显然ai前面有k个元素时,他的贡献就是A[k]*ai*dp[i][k]*A[n-1-k](A[]为排列数)
  3. 怎么算dp的贡献呢,显然每个元素的贡献取决有他有多少位,设元素p有b位,显然有转移方程式dp[i][j+1]=dp[i][j]*10^{b}(无论他插在j个元素里的哪个位置,显然都是使dp[i][j]的贡献在10进制上左移了b位(即*10^b)
  4. 因为每个数讨论前面的数的情况是不能有他自己的,显然我们每个元素都要自己做一次dp,用背包优化可以设当降低复杂度
  1. #include <bits/stdc++.h>
  2. using namespace std;
  3. #define ll long long
  4. #define endl "\n"
  5. #define int long long
  6. typedef pair<int, int> pii;
  7. const int N = 2000+20;
  8. int mycnt(int x)
  9. {
  10. int ans=0;
  11. while(x)ans++,x/=10;
  12. return ans;
  13. }
  14. const int mod=1e9+7;
  15. pii a[N];
  16. int pre[N],A[N];//pre表示10的几次幂,A是排列数
  17. void mysolve()
  18. {
  19. int n;
  20. cin>>n;
  21. for(int i=1; i<=n; ++i)cin>>a[i].first,a[i].second=mycnt(a[i].first);
  22. int ans=0;
  23. for(int i=1; i<=n; ++i)
  24. {
  25. vector<int>dp(n+1);//讨论当前元素a[i]在所有排列的总贡献,先给他处理下dp
  26. dp[0]=1;
  27. for(int j=1,p=0; j<=n; ++j)if(i!=j)//背包优化
  28. {
  29. p++;//p表示处理了p个数
  30. for(int k=p; k; --k)dp[k]=(dp[k]+dp[k-1]*pre[a[j].second]%mod)%mod;
  31. }
  32. for(int j=0; j<n; ++j)//枚举ai前面有几个元素的情况时的贡献
  33. ans=(ans+a[i].first*A[n-j-1]%mod*dp[j]%mod*A[j]%mod)%mod;
  34. }
  35. cout<<ans<<endl;
  36. }
  37. int32_t main()
  38. {
  39. std::ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
  40. pre[0]=1,A[0]=1;
  41. for(int i=1; i<=2000; ++i)pre[i]=pre[i-1]*10%mod,A[i]=A[i-1]*i%mod;
  42. mysolve();
  43. system("pause");
  44. return 0;
  45. }

寻宝

题意

  • 探险队获得了一张藏宝图,藏宝图中有n个洞穴,还有n−1条道路连通这n个洞穴,对于任意两个不同的洞穴,都可以通过道路相互到达。
  • 在藏宝图中,一些洞穴里被标记有宝藏。
  • 探险队决定选取一些有宝物的洞穴作为寻宝计划,他们会从寻宝计划中的一个洞穴出发,依次到达每个寻宝计划中其他的洞穴收集宝物,最后再回到出发的洞穴,在这个过程中,他们会选择路程最少的寻宝路线。可以证明,无论从寻宝计划中的哪个洞穴出发,最终的最小路程都是相同的。
  • 一次探险的路程是探险中经过的道路数量。
  • 现在你需要计算对于每个k(1≤k≤n),在探险队任选k个有宝物的洞穴作为寻宝计划时,他们要走的最小路程最多是多少,最少是多少?

思路:

  1. 树上dp,以最大值为例,dp[u][k]维护好处理到子树u,k个宝藏的最长距离。
  2. 而答案就是每次子树给入父节点时更新答案
  1. #include <bits/stdc++.h>
  2. using namespace std;
  3. #define ll long long
  4. #define endl "\n"
  5. const int INF = 0x3f3f3f3f; //int型的INF
  6. const double eps=1e-9;
  7. const int N = 3e3 + 10;
  8. vector<int>edge[N];
  9. int mxans[N],mnans[N],mxdp[N][N],mndp[N][N],sz[N];
  10. bool a[N];
  11. int n;
  12. void dfs(int u,int f)
  13. {
  14. sz[u]=1;
  15. for(auto v:edge[u])if(v!=f)
  16. {
  17. dfs(v,u);
  18. for(int i=sz[u]; ~i; --i)for(int j=sz[v]; j; --j)
  19. {
  20. mxdp[u][i+j]=max(mxdp[u][i+j],mxdp[u][i]+mxdp[v][j]+2);
  21. mndp[u][i+j]=min(mndp[u][i+j],mndp[u][i]+mndp[v][j]+2);
  22. if(i)
  23. {
  24. mxans[i+j]=max(mxans[i+j],mxdp[u][i]+mxdp[v][j]+2);//这里不写mxdp[u][i+j],因为u的i+j是可以由i=0,j>0更新得到,但是子树合并必须ij均大于0合并,才算是最短路径的最大值,否者不符合最短路径的定义
  25. mnans[i+j]=min(mnans[i+j],mndp[u][i]+mndp[v][j]+2);
  26. }
  27. }
  28. sz[u]+=sz[v];
  29. }
  30. }
  31. void mysolve()
  32. {
  33. cin>>n;
  34. for(int i=1; i<=n; ++i)
  35. {
  36. mxans[i]=-INF;
  37. mnans[i]=INF;
  38. for(int j=1; j<=n; ++j)mxdp[i][j]=-INF,mndp[i][j]=INF;
  39. }
  40. for(int i=1; i<=n; ++i)
  41. {
  42. cin>>a[i];
  43. if(a[i])mxdp[i][1]=mndp[i][1]=0,mxans[1]=mnans[1]=0;
  44. }
  45. int x,y;
  46. for(int i=1; i<n; ++i)cin>>x>>y,edge[x].push_back(y),edge[y].push_back(x);
  47. dfs(1,0);
  48. for(int i=1; i<=n; ++i)
  49. {
  50. if(mxans[i]>=0)cout<<mxans[i]<<" "<<mnans[i]<<endl;
  51. else
  52. cout<<-1<<" "<<-1<<endl;
  53. }
  54. }
  55. int32_t main()
  56. {
  57. std::ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
  58. ll t=1;
  59. //cin >> t;
  60. while (t--)
  61. {
  62. mysolve();
  63. }
  64. system("pause");
  65. return 0;
  66. }

数三角形

题意:

  • 作为一名学霸,当然要学会数数,不仅要会数正方体,也要会数三角形。
  • 在二维平面上存在位置互不相同的n个点,这些点的横纵坐标均为整数,第i个点的坐标为(ai​,bi​)。你需要计算存在多少个三元组(i,j,k)(1≤i<j<k≤n)满足i,j,k三个点可以构成面积大于0的等腰三角形。
  • 当然了,为了简化一些计算量,本题还满足一个特殊性质:这n个点仅存在于两条距离不超过10的平行于x轴的直线上。

思路:

  1. 首先清楚三角形有2种,一种有一条腰在线上,一种是两条腰都不在线上。显然这两种不冲突(因为格点上不存在等边三角形)
  2. 第一种情况,如果腰只有一条不在线上,那么首先他是完全平方数,而可以匹配的完全平方数的对数是很少的,这个可以暴力枚举
  3. 对于第二种情况,需要枚举i+j=2*k,暴力是O(n),发现ai<=1e5,那么用NNT实现nlogn求寻i+j的各种组合数
  1. #include <bits/stdc++.h>
  2. using namespace std;
  3. #define ll long long
  4. #define int long long
  5. typedef pair<int, int> pii;
  6. const int N = 3e5 + 10,G = 3, Gi = 332748118;
  7. const int mod=998244353;
  8. vector<pii>pre[15];
  9. int limit,L;
  10. int r[N];
  11. void init()
  12. {
  13. pre[3].push_back({4,5+4});
  14. pre[4].push_back({3,5+3});
  15. pre[5].push_back({12,13+12});
  16. pre[6].push_back({8,10+8});
  17. pre[7].push_back({24,25+24});
  18. pre[8].push_back({6,10+6});
  19. pre[8].push_back({15,17+15});
  20. pre[9].push_back({12,15+12});
  21. pre[9].push_back({40,41+40});
  22. pre[10].push_back({24,26+24});
  23. }
  24. ll fastmi(ll base, ll power)
  25. {
  26. ll ans = 1;
  27. while (power)
  28. {
  29. if (power & 1)ans=ans*base%mod;
  30. base=base*base%mod;
  31. power >>=1;
  32. }
  33. return ans;
  34. }
  35. inline void NTT(ll *A, int type)
  36. {
  37. for(int i = 0; i < limit; i++)
  38. if(i < r[i]) swap(A[i], A[r[i]]);
  39. for(int mid = 1; mid < limit; mid <<= 1)
  40. {
  41. ll Wn = fastmi( type == 1 ? G : Gi, (mod - 1) / (mid << 1));
  42. for(int j = 0; j < limit; j += (mid << 1))
  43. {
  44. ll w = 1;
  45. for(int k = 0; k < mid; k++, w = (w * Wn) % mod)
  46. {
  47. int x = A[j + k], y = w * A[j + k + mid] % mod;
  48. A[j + k] = (x + y) % mod,
  49. A[j + k + mid] = (x - y + mod) % mod;
  50. }
  51. }
  52. }
  53. }
  54. ll aa[N],bb[N];
  55. void mysolve()
  56. {
  57. vector<int>a1,b1;
  58. unordered_map<int,int>a,b;
  59. int n,A,B;
  60. cin>>n>>A>>B;
  61. int x1,y1;
  62. for(int i=1; i<=n; ++i)
  63. {
  64. cin>>x1>>y1;
  65. if(y1==A)a[x1]=1,a1.push_back(x1);
  66. else b[x1]=1,b1.push_back(x1);
  67. }
  68. int ans=0;
  69. int d=abs(A-B);
  70. sort(a1.begin(),a1.end()),sort(b1.begin(),b1.end());
  71. for(auto v:a1)//暴力枚举第一种
  72. {
  73. for(pii u:pre[d])
  74. {
  75. if(b.count(v+u.first)&&b.count(v+u.second))ans++;
  76. if(b.count(v-u.first)&&b.count(v-u.second))ans++;
  77. if(b.count(v+u.first)&&b.count(v-(u.second-2*u.first)))ans++;
  78. if(b.count(v-u.first)&&b.count(v+(u.second-2*u.first)))ans++;
  79. }
  80. if(b.count(v+d)&&b.count(v))ans++;
  81. if(b.count(v-d)&&b.count(v))ans++;
  82. ans%=mod;
  83. }
  84. for(auto v:b1)
  85. {
  86. for(pii u:pre[d])
  87. {
  88. if(a.count(v+u.first)&&a.count(v+u.second))ans++;
  89. if(a.count(v-u.first)&&a.count(v-u.second))ans++;
  90. if(a.count(v+u.first)&&a.count(v-(u.second-2*u.first)))ans++;
  91. if(a.count(v-u.first)&&a.count(v+(u.second-2*u.first)))ans++;
  92. }
  93. if(a.count(v+d)&&a.count(v))ans++;
  94. if(a.count(v-d)&&a.count(v))ans++;
  95. ans%=mod;
  96. }
  97. limit=1,L=0;//NNT计算i+j枚举后各个数的出现次数
  98. while(limit<=2e5)limit<<=1,L++;
  99. for(int i = 0; i < limit; i++)
  100. r[i] = (r[i >> 1] >> 1) | ((i & 1) << (L - 1));
  101. for(int i=0; i<=1e5; ++i)
  102. {
  103. if(a[i])aa[i]=1;
  104. if(b[i])bb[i]=1;
  105. }
  106. NTT(aa,1),NTT(bb,1);
  107. for(int i=0; i<limit; ++i)aa[i]=aa[i]*aa[i]%mod,bb[i]=bb[i]*bb[i]%mod;//多项式a*a
  108. NTT(aa,-1),NTT(bb,-1);
  109. ll inv=fastmi(limit,mod-2);
  110. for(int i=0; i<=2e5; ++i)aa[i]=aa[i]*inv%mod,bb[i]=bb[i]*inv%mod;
  111. int res=0;
  112. for(int i=0; i<=1e5; ++i)
  113. {
  114. if(a[i])res+=bb[i<<1]-b[i];//减去i+i的情况
  115. if(b[i])res+=aa[i<<1]-a[i];
  116. }
  117. ans=(ans+res/2)%mod;//因为i+j与j+i各自算了一次,要除2
  118. cout<<ans<<endl;
  119. }
  120. int32_t main()
  121. {
  122. std::ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
  123. ll t=1;
  124. //cin >> t;
  125. while (t--)
  126. {
  127. init();
  128. mysolve();
  129. }
  130. system("pause");
  131. return 0;
  132. }

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/不正经/article/detail/443340
推荐阅读
相关标签
  

闽ICP备14008679号