赞
踩
首先讲了数据的预处理,包括离散化和前缀和的东西。
前缀和:
洛谷P3406
关键差分公式为了使区间[l,r]区间加上1 我们需要:
前缀和公式:
其中arr表示数组,若我们只想用差分数组出来前缀和,这个arr可以不用。
为了让[x1,y1]到[x2,y2]的区间加1
二维差分公式:
二维前缀和:
二维差分需要二位前缀和来恢复。
洛谷P3406
- #include <bits/stdc++.h>
- #define int long long
- using namespace std;
- int32_t main(){
- int n,m;cin>>n>>m;
- vector<int> mv;
- for(int i=0;i<m;i++){
- int t;cin>>t;
- mv.push_back(t);
- }
- vector<vector<int> > arrmv(n,vector<int>(3,0));
- for(int i=0;i<n-1;i++){
- cin>>arrmv[i][0]>>arrmv[i][1]>>arrmv[i][2];
- }
- int dif[n+1];
- memset(dif,0,sizeof(dif));
- for(int i=0;i<m-1;i++){
- int a=mv[i];
- int b=mv[i+1];
- if(b<a)swap(a,b);
- dif[a]+=1;
- dif[b]-=1;
- }
- for(int i=1;i<n;i++){
- dif[i]+=dif[i-1];}
- // for(int i=1;i<=n-1;i++)cerr<<dif[i]<<endl;
-
- int ans=0;
- for(int i=1;i<=n-1;i++){
- if(dif[i]*arrmv[i-1][0]>(dif[i]*arrmv[i-1][1]+arrmv[i-1][2])){
- ans+=dif[i]*arrmv[i-1][1]+arrmv[i-1][2];}
- else ans+=dif[i]*arrmv[i-1][0];
- }
- cout<<ans<<endl;
-
- return 0;
- }

洛谷1115
- #include <bits/stdc++.h>
- using namespace std;
- const int inf=1e9;
- int main(){
- int n;cin>>n;
- vector<int> arrmv(n,0);
- for(int i=0;i<n;i++)
- cin>>arrmv[i];
- vector<int> sumarr(n+1,0);
- for(int i=0;i<n;i++){
- sumarr[i+1]+=sumarr[i]+arrmv[i];
- }
- int t=sumarr[0];
- int ans=-inf;
- for(int i=1;i<=n;i++){
-
- ans=max(ans,sumarr[i]-t);
- t=min(sumarr[i],t);
- }
- cout<<ans<<endl;
- return 0;
- }

洛谷3397
- #include <bits/stdc++.h>
- using namespace std;
- const int MAXN=1e3+10;
- int arr[MAXN][MAXN];
- int main(){
- memset(arr,0,sizeof(arr));
- int n,m;cin>>n>>m;
- for(int i=0;i<m;i++){
- int x1,y1,x2,y2;
- cin>>x1>>y1>>x2>>y2;
- x1-=1;y1-=1;x2-=1;y2-=1;
- arr[x1][y1]+=1;
- arr[x2+1][y2+1]+=1;
- arr[x2+1][y1]-=1;
- arr[x1][y2+1]-=1;
- }
- for(int i=0;i<n;i++)
- for(int j=0;j<n;j++){
- if(i)arr[i][j]+=arr[i-1][j];
- if(j)arr[i][j]+=arr[i][j-1];
- if(i &&j)arr[i][j]-=arr[i-1][j-1];
- }
- for(int i=0;i<n;i++){
- for(int j=0;j<n;j++){
- if(j)cout<<" ";
-
- cout<<arr[i][j];
- }
- cout<<endl;
- }
-
- return 0;
- }

树上对点或者边差分
当我们需要频繁地对树上任意路径的边权值或者点权值加w时候,可以使用这一个方法。复杂度为o(nlogn).主要复杂度在计算lca上面。
首先我们写出树上对点的差分公式:
其中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数组指的是当前节点指向父亲节点的边。为了从差分恢复,我们需要计算树上前缀和。代码如下:
- int dfs(int u){
- flag[u]=1; //走过打个标记
- for(int i=0;i<(int)tree[u].size();i++){
- int nx=tree[u][i];
- if(flag[nx])continue;
- diff[u]+=dfs(nx);
- }
- return diff[u];
- }
洛谷3258
这题是很好的对边差分的题目。这题看似是对点进行差分,但是我们发现假如使用对点进行差分的话,需要特判而且情况特别多。这里我们可以转换为对边差分。对于除了终止节点的其它节点有如下公式:
表示当前点的权值为相邻边走过的次数+1再除以2.对于终止节点,分子的+1需要去掉。
- #include <bits/stdc++.h>
- #define MAXN 300010
- #define level 19
- using namespace std;
- vector <int> tree[MAXN];
- int depth[MAXN];
- int parent[MAXN][level];
- // pre-compute the depth for each node and their
- // first parent(2^0th parent)
- // time complexity : O(n)
- void dfs(int cur, int prev)
- {
- depth[cur] = depth[prev] + 1;
- parent[cur][0] = prev;
- for (int i=0; i<tree[cur].size(); i++)
- {
- if (tree[cur][i] != prev)
- dfs(tree[cur][i], cur);
- }
- }
-
- // Dynamic Programming Sparse Matrix Approach
- // populating 2^i parent for each node
- // Time complexity : O(nlogn)
- void precomputeSparseMatrix(int n)
- {
- for (int i=1; i<level; i++)
- {
- for (int node = 1; node <= n; node++)
- {
- if (parent[node][i-1] != -1)
- parent[node][i] =
- parent[parent[node][i-1]][i-1];
- }
- }
- }
-
- // Returning the LCA of u and v
- // Time complexity : O(log n)
- int lca(int u, int v)
- {
- if (depth[v] < depth[u])
- swap(u, v);
-
- int diff = depth[v] - depth[u];
-
- // Step 1 of the pseudocode
- for (int i=0; i<level; i++)
- if ((diff>>i)&1)
- v = parent[v][i];
-
- // now depth[u] == depth[v]
- if (u == v)
- return u;
-
- // Step 2 of the pseudocode
- for (int i=level-1; i>=0; i--)
- if (parent[u][i] != parent[v][i])
- {
- u = parent[u][i];
- v = parent[v][i];
- }
-
- return parent[u][0];
- }
-
- void addEdge(int u,int v)
- {
- tree[u].push_back(v);
- tree[v].push_back(u);
- }
- vector<int> diff(MAXN,0);
- vector<int> flag(MAXN,0);
- int dfs2(int u){
- flag[u]=1;
- for(int i=0;i<(int)tree[u].size();i++){
- int nx=tree[u][i];
- if(flag[nx])continue;
- diff[u]+=dfs2(nx);
- }
- return diff[u];
- }
-
-
-
- int main(){
- memset(parent,-1,sizeof(parent));
- int n;cin>>n;
-
- vector<int> arrmv(n);
- for(int i=0;i<n;i++)cin>>arrmv[i];
- for(int i=0;i<n-1;i++){
- int a,b;cin>>a>>b;
- addEdge(a,b);
- }
- depth[0]=0;
- dfs(1,0);
-
- precomputeSparseMatrix(n);
- for(int i=0;i<n-1;i++){
- int a,b;
- a=arrmv[i];
- b=arrmv[i+1];
- int lcaa=lca(a,b);
- diff[a]+=1;
- diff[b]+=1;
- diff[lcaa]-=2;
-
- }
- dfs2(1);
- int ans[n+1];
- for(int i=1;i<=n;i++){
- ans[i]=diff[i];
- for(int ii=0;ii<(int)tree[i].size();ii++){
- int nx=tree[i][ii];
- if(nx==parent[i][0])continue;
- ans[i]+=diff[nx];
- }
- }
- for(int i=1;i<=n;i++){
- cout<<ans[i]<<endl;
- }
-
- return 0;
- }

关于贪心:
序列贪心。序列贪心指的是,在一串序列中,我们需要通过不断安排元素的位置使得某一个量最大或者最小。做这种题假若从贪心入手,我们可以这样考虑。我们就假设有两个元素a和b分别将它们排列为a,b和b,a,然后比较量的关系。比如最大值之间的最小值。我们选出max(order(ab)),max(order(ba))做比较,得到最优的一个量后,我们得到一个关键字用作sort函数里面的cmp.
洛谷1842:
- #include <bits/stdc++.h>
- #define int long long
- using namespace std;
- bool cmp(pair<int,int> &fir ,pair<int,int> &las){
- if(fir.first+fir.second>las.first+las.second){
- return true;
- }else return false;
- }
- const int inf=1e18;
- int32_t main(){
- int n;cin>>n;
- vector<pair<int,int>> arrmv(n);
- for(int i=0;i<n;i++){
- int a,b;cin>>a>>b;
- arrmv[i]=make_pair(a,b);
- }
- sort(arrmv.begin(),arrmv.end(),cmp);
- reverse(arrmv.begin(),arrmv.end());
- vector<int> presum(n+2,0);
- for(int i=1;i<=n;i++){
- presum[i]+=presum[i-1]+arrmv[i-1].first;
- }
- reverse(arrmv.begin(),arrmv.end());
- reverse(presum.begin(),presum.end());
- int ans=-inf;
- for(int i=0;i<n;i++){
- ans=max(ans,presum[i+2]-arrmv[i].second);
- }
- cout<<ans<<endl;
- return 0;
- }

带反悔的贪心
所谓带反悔的贪心就是,我们一直选择贪心策略,但是,我们遇到一个有可能更优的策略时,我们去掉前面做的一个相对更劣的策略,加入这个新的策略,最后我们会全局最优。一般使用c++的优先队列来记录以前做过的策略,每次需要做新策率时候我们就比较即可。
洛谷P4053
- #include <bits/stdc++.h>
- #define int long long
- using namespace std;
- int32_t main(){
- int n;cin>>n;
- vector<pair<int,int>> mv;
- for(int i=0;i<n;i++){
- int a,b;cin>>a>>b;
- mv.push_back(make_pair(b,a));
- }
- sort(mv.begin(),mv.end(),less<pair<int,int>>());
- priority_queue<int,vector<int>,less<int>> pq;
- int lstime=0;
- for(int i=0;i<n;i++){
- if(lstime+mv[i].second<=mv[i].first){
- lstime=lstime+mv[i].second;
- pq.push(mv[i].second);
- }else{
- if(pq.size()){
- int no=pq.top();
- if(mv[i].second <no &&lstime-no+mv[i].second<=mv[i].first){
- pq.pop();
- pq.push(mv[i].second);
- lstime=lstime-no;
- lstime+=mv[i].second;
- }
- }
- }
- }
- cout<<pq.size()<<endl;
- return 0;
- }

洛谷1230
- #include <bits/stdc++.h>
- using namespace std;
- int main(){
- int m,n;cin>>m>>n;
- vector<pair<int,int>> arrmv(n);
- for(int i=0;i<n;i++){
- cin>>arrmv[i].first;
- }
- int sum=0;
- for(int i=0;i<n;i++){
- cin>>arrmv[i].second;
- sum+=arrmv[i].second;
- }
- sort(arrmv.begin(),arrmv.end(),less<pair<int,int>>());
- priority_queue<int,vector<int>,greater<int>> pq;
- int take=0;
- for(int i=0;i<n;i++){
- if(arrmv[i].first>take){
- take++;
- pq.push(arrmv[i].second);
- }else{
- int no=pq.top();if(arrmv[i].second>no){
- pq.pop();pq.push(arrmv[i].second);
- }
- }
- }
- while(!pq.empty()){
- int no=pq.top();
- pq.pop();
- sum-=no;
- }
- int ans=m-sum;
- cout<<ans<<endl;
-
-
- return 0;
- }

Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。