当前位置:   article > 正文

洛谷2019秋令营 网课第一节_洛谷网课

洛谷网课

首先讲了数据的预处理,包括离散化和前缀和的东西。

前缀和

洛谷P3406

关键差分公式为了使区间[l,r]区间加上1 我们需要:

                                                                       sum[l]+=1sum[r+1]=1

前缀和公式:

                                                                      sum[l]+=sum[l1]+arr[l]

其中arr表示数组,若我们只想用差分数组出来前缀和,这个arr可以不用。

为了让[x1,y1]到[x2,y2]的区间加1

二维差分公式:

                                                                        sum[x1][y1]+=1sum[x2+1][y2+1]+=1sum[x1][y2+1]=1sum[x2+1][y1]=1

二维前缀和:

                                                                       sum[x][y]+=sum[x1][y]+sum[x][y1]sum[x1][y1]

二维差分需要二位前缀和来恢复。

洛谷P3406

  1. #include <bits/stdc++.h>
  2. #define int long long
  3. using namespace std;
  4. int32_t main(){
  5. int n,m;cin>>n>>m;
  6. vector<int> mv;
  7. for(int i=0;i<m;i++){
  8. int t;cin>>t;
  9. mv.push_back(t);
  10. }
  11. vector<vector<int> > arrmv(n,vector<int>(3,0));
  12. for(int i=0;i<n-1;i++){
  13. cin>>arrmv[i][0]>>arrmv[i][1]>>arrmv[i][2];
  14. }
  15. int dif[n+1];
  16. memset(dif,0,sizeof(dif));
  17. for(int i=0;i<m-1;i++){
  18. int a=mv[i];
  19. int b=mv[i+1];
  20. if(b<a)swap(a,b);
  21. dif[a]+=1;
  22. dif[b]-=1;
  23. }
  24. for(int i=1;i<n;i++){
  25. dif[i]+=dif[i-1];}
  26. // for(int i=1;i<=n-1;i++)cerr<<dif[i]<<endl;
  27. int ans=0;
  28. for(int i=1;i<=n-1;i++){
  29. if(dif[i]*arrmv[i-1][0]>(dif[i]*arrmv[i-1][1]+arrmv[i-1][2])){
  30. ans+=dif[i]*arrmv[i-1][1]+arrmv[i-1][2];}
  31. else ans+=dif[i]*arrmv[i-1][0];
  32. }
  33. cout<<ans<<endl;
  34. return 0;
  35. }

洛谷1115

  1. #include <bits/stdc++.h>
  2. using namespace std;
  3. const int inf=1e9;
  4. int main(){
  5. int n;cin>>n;
  6. vector<int> arrmv(n,0);
  7. for(int i=0;i<n;i++)
  8. cin>>arrmv[i];
  9. vector<int> sumarr(n+1,0);
  10. for(int i=0;i<n;i++){
  11. sumarr[i+1]+=sumarr[i]+arrmv[i];
  12. }
  13. int t=sumarr[0];
  14. int ans=-inf;
  15. for(int i=1;i<=n;i++){
  16. ans=max(ans,sumarr[i]-t);
  17. t=min(sumarr[i],t);
  18. }
  19. cout<<ans<<endl;
  20. return 0;
  21. }

洛谷3397

  1. #include <bits/stdc++.h>
  2. using namespace std;
  3. const int MAXN=1e3+10;
  4. int arr[MAXN][MAXN];
  5. int main(){
  6. memset(arr,0,sizeof(arr));
  7. int n,m;cin>>n>>m;
  8. for(int i=0;i<m;i++){
  9. int x1,y1,x2,y2;
  10. cin>>x1>>y1>>x2>>y2;
  11. x1-=1;y1-=1;x2-=1;y2-=1;
  12. arr[x1][y1]+=1;
  13. arr[x2+1][y2+1]+=1;
  14. arr[x2+1][y1]-=1;
  15. arr[x1][y2+1]-=1;
  16. }
  17. for(int i=0;i<n;i++)
  18. for(int j=0;j<n;j++){
  19. if(i)arr[i][j]+=arr[i-1][j];
  20. if(j)arr[i][j]+=arr[i][j-1];
  21. if(i &&j)arr[i][j]-=arr[i-1][j-1];
  22. }
  23. for(int i=0;i<n;i++){
  24. for(int j=0;j<n;j++){
  25. if(j)cout<<" ";
  26. cout<<arr[i][j];
  27. }
  28. cout<<endl;
  29. }
  30. return 0;
  31. }

树上对点或者边差分

当我们需要频繁地对树上任意路径的边权值或者点权值加w时候,可以使用这一个方法。复杂度为o(nlogn).主要复杂度在计算lca上面。

首先我们写出树上对点的差分公式:

                                                         diff[u]+=wdiff[v]+=wdiff[lca(u,v)]=wdiff[parent[lca(u,v)]]=w

其中diff为差分数组,记录的是每个点的权值变化。后面需要使用树上前缀和恢复。lca为最近邻公共祖先,一般使用倍增的方法实现:

https://www.geeksforgeeks.org/lca-for-general-or-n-ary-trees-sparse-matrix-dp-approach-onlogn-ologn/

parent代表父亲数组,记录的是当前节点的父亲。在计算lca时候,在用倍增计算父亲时,我们可以计算当前节点的第2^i个父亲,所以这个parent数组应该是在计算lca时候得到的。

注意,u和v下标都从1开始,这样方便计算。主要是因为里面用了parent数组,假如从1做下标,我们就免去了处理0点的parent的特判。然后是树上对边差分,公式如下:

                                                              diff[u]+=wdiff[v]+=wdiff[lca(u,v)]=2w

注意这里的diff数组指的是当前节点指向父亲节点的边。为了从差分恢复,我们需要计算树上前缀和。代码如下:

  1. int dfs(int u){
  2. flag[u]=1; //走过打个标记
  3. for(int i=0;i<(int)tree[u].size();i++){
  4. int nx=tree[u][i];
  5. if(flag[nx])continue;
  6. diff[u]+=dfs(nx);
  7. }
  8. return diff[u];
  9. }

洛谷3258

这题是很好的对边差分的题目。这题看似是对点进行差分,但是我们发现假如使用对点进行差分的话,需要特判而且情况特别多。这里我们可以转换为对边差分。对于除了终止节点的其它节点有如下公式:

                                                                       (1+icnti)/2

表示当前点的权值为相邻边走过的次数+1再除以2.对于终止节点,分子的+1需要去掉。

  1. #include <bits/stdc++.h>
  2. #define MAXN 300010
  3. #define level 19
  4. using namespace std;
  5. vector <int> tree[MAXN];
  6. int depth[MAXN];
  7. int parent[MAXN][level];
  8. // pre-compute the depth for each node and their
  9. // first parent(2^0th parent)
  10. // time complexity : O(n)
  11. void dfs(int cur, int prev)
  12. {
  13. depth[cur] = depth[prev] + 1;
  14. parent[cur][0] = prev;
  15. for (int i=0; i<tree[cur].size(); i++)
  16. {
  17. if (tree[cur][i] != prev)
  18. dfs(tree[cur][i], cur);
  19. }
  20. }
  21. // Dynamic Programming Sparse Matrix Approach
  22. // populating 2^i parent for each node
  23. // Time complexity : O(nlogn)
  24. void precomputeSparseMatrix(int n)
  25. {
  26. for (int i=1; i<level; i++)
  27. {
  28. for (int node = 1; node <= n; node++)
  29. {
  30. if (parent[node][i-1] != -1)
  31. parent[node][i] =
  32. parent[parent[node][i-1]][i-1];
  33. }
  34. }
  35. }
  36. // Returning the LCA of u and v
  37. // Time complexity : O(log n)
  38. int lca(int u, int v)
  39. {
  40. if (depth[v] < depth[u])
  41. swap(u, v);
  42. int diff = depth[v] - depth[u];
  43. // Step 1 of the pseudocode
  44. for (int i=0; i<level; i++)
  45. if ((diff>>i)&1)
  46. v = parent[v][i];
  47. // now depth[u] == depth[v]
  48. if (u == v)
  49. return u;
  50. // Step 2 of the pseudocode
  51. for (int i=level-1; i>=0; i--)
  52. if (parent[u][i] != parent[v][i])
  53. {
  54. u = parent[u][i];
  55. v = parent[v][i];
  56. }
  57. return parent[u][0];
  58. }
  59. void addEdge(int u,int v)
  60. {
  61. tree[u].push_back(v);
  62. tree[v].push_back(u);
  63. }
  64. vector<int> diff(MAXN,0);
  65. vector<int> flag(MAXN,0);
  66. int dfs2(int u){
  67. flag[u]=1;
  68. for(int i=0;i<(int)tree[u].size();i++){
  69. int nx=tree[u][i];
  70. if(flag[nx])continue;
  71. diff[u]+=dfs2(nx);
  72. }
  73. return diff[u];
  74. }
  75. int main(){
  76. memset(parent,-1,sizeof(parent));
  77. int n;cin>>n;
  78. vector<int> arrmv(n);
  79. for(int i=0;i<n;i++)cin>>arrmv[i];
  80. for(int i=0;i<n-1;i++){
  81. int a,b;cin>>a>>b;
  82. addEdge(a,b);
  83. }
  84. depth[0]=0;
  85. dfs(1,0);
  86. precomputeSparseMatrix(n);
  87. for(int i=0;i<n-1;i++){
  88. int a,b;
  89. a=arrmv[i];
  90. b=arrmv[i+1];
  91. int lcaa=lca(a,b);
  92. diff[a]+=1;
  93. diff[b]+=1;
  94. diff[lcaa]-=2;
  95. }
  96. dfs2(1);
  97. int ans[n+1];
  98. for(int i=1;i<=n;i++){
  99. ans[i]=diff[i];
  100. for(int ii=0;ii<(int)tree[i].size();ii++){
  101. int nx=tree[i][ii];
  102. if(nx==parent[i][0])continue;
  103. ans[i]+=diff[nx];
  104. }
  105. }
  106. for(int i=1;i<=n;i++){
  107. cout<<ans[i]<<endl;
  108. }
  109. return 0;
  110. }

关于贪心:

序列贪心。序列贪心指的是,在一串序列中,我们需要通过不断安排元素的位置使得某一个量最大或者最小。做这种题假若从贪心入手,我们可以这样考虑。我们就假设有两个元素a和b分别将它们排列为a,b和b,a,然后比较量的关系。比如最大值之间的最小值。我们选出max(order(ab)),max(order(ba))做比较,得到最优的一个量后,我们得到一个关键字用作sort函数里面的cmp.

洛谷1842:

  1. #include <bits/stdc++.h>
  2. #define int long long
  3. using namespace std;
  4. bool cmp(pair<int,int> &fir ,pair<int,int> &las){
  5. if(fir.first+fir.second>las.first+las.second){
  6. return true;
  7. }else return false;
  8. }
  9. const int inf=1e18;
  10. int32_t main(){
  11. int n;cin>>n;
  12. vector<pair<int,int>> arrmv(n);
  13. for(int i=0;i<n;i++){
  14. int a,b;cin>>a>>b;
  15. arrmv[i]=make_pair(a,b);
  16. }
  17. sort(arrmv.begin(),arrmv.end(),cmp);
  18. reverse(arrmv.begin(),arrmv.end());
  19. vector<int> presum(n+2,0);
  20. for(int i=1;i<=n;i++){
  21. presum[i]+=presum[i-1]+arrmv[i-1].first;
  22. }
  23. reverse(arrmv.begin(),arrmv.end());
  24. reverse(presum.begin(),presum.end());
  25. int ans=-inf;
  26. for(int i=0;i<n;i++){
  27. ans=max(ans,presum[i+2]-arrmv[i].second);
  28. }
  29. cout<<ans<<endl;
  30. return 0;
  31. }

带反悔的贪心

所谓带反悔的贪心就是,我们一直选择贪心策略,但是,我们遇到一个有可能更优的策略时,我们去掉前面做的一个相对更劣的策略,加入这个新的策略,最后我们会全局最优。一般使用c++的优先队列来记录以前做过的策略,每次需要做新策率时候我们就比较即可。

洛谷P4053

  1. #include <bits/stdc++.h>
  2. #define int long long
  3. using namespace std;
  4. int32_t main(){
  5. int n;cin>>n;
  6. vector<pair<int,int>> mv;
  7. for(int i=0;i<n;i++){
  8. int a,b;cin>>a>>b;
  9. mv.push_back(make_pair(b,a));
  10. }
  11. sort(mv.begin(),mv.end(),less<pair<int,int>>());
  12. priority_queue<int,vector<int>,less<int>> pq;
  13. int lstime=0;
  14. for(int i=0;i<n;i++){
  15. if(lstime+mv[i].second<=mv[i].first){
  16. lstime=lstime+mv[i].second;
  17. pq.push(mv[i].second);
  18. }else{
  19. if(pq.size()){
  20. int no=pq.top();
  21. if(mv[i].second <no &&lstime-no+mv[i].second<=mv[i].first){
  22. pq.pop();
  23. pq.push(mv[i].second);
  24. lstime=lstime-no;
  25. lstime+=mv[i].second;
  26. }
  27. }
  28. }
  29. }
  30. cout<<pq.size()<<endl;
  31. return 0;
  32. }

洛谷1230

  1. #include <bits/stdc++.h>
  2. using namespace std;
  3. int main(){
  4.     int m,n;cin>>m>>n;
  5.     vector<pair<int,int>> arrmv(n);
  6.     for(int i=0;i<n;i++){
  7.         cin>>arrmv[i].first;
  8.     }
  9.     int sum=0;
  10.     for(int i=0;i<n;i++){
  11.         cin>>arrmv[i].second;
  12.         sum+=arrmv[i].second;
  13.     }
  14.     sort(arrmv.begin(),arrmv.end(),less<pair<int,int>>());
  15.     priority_queue<int,vector<int>,greater<int>> pq;
  16.     int take=0;
  17.     for(int i=0;i<n;i++){
  18.         if(arrmv[i].first>take){
  19.             take++;
  20.             pq.push(arrmv[i].second);
  21.         }else{
  22.             int no=pq.top();if(arrmv[i].second>no){
  23.                 pq.pop();pq.push(arrmv[i].second);
  24.             }
  25.         }
  26.     }
  27.     while(!pq.empty()){
  28.         int no=pq.top();
  29.         pq.pop();
  30.         sum-=no;
  31.     }
  32.     int ans=m-sum;
  33.     cout<<ans<<endl;
  34.         
  35.     return 0;
  36. }

 

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

闽ICP备14008679号