首先讲了数据的预处理,包括离散化和前缀和的东西。
前缀和:
洛谷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;
}