题解:P7294 [USACO21JAN] Minimum Cost Paths P
很显然的整体 dp 的形式。
我们设
因为贡献函数
每次从
现在考虑列内的转移。上面说了
由于第二维是
直接的思路是维护一个差分数组,这样子非常方便更新。但是由于不是问
于是考虑维护
每次修改由于我们不是直接维护的差分数组,所以可以直接用 pop,由于每列只会进队列 pop 到满足题意之后要二分寻找一下,然后插入新的端点。
每次询问就是离线处理,二分找到询问点在栈中哪两个端点之中,然后求值。
时间复杂度
#include<bits/stdc++.h>
#define pb emplace_back
#define fi first
#define se second
#define mp make_pair
using namespace std;
typedef long long ll;
const int maxn=2e5+10;
void cmax(int &x,int y){ x=x>y?x:y; }
void cmin(int &x,int y){ x=x<y?x:y; }
ll V(int x){ return 1ll*x*x; } int c[maxn];
struct node{
int x,tim; ll deta;
bool operator < (const node &rhs) const { return mp(x,tim)<mp(rhs.x,rhs.tim);}
ll get(int x,int t){ return V(x)*(t-tim)+1ll*x*c[tim]+deta; }
}st[maxn]; int top=0;
int n,m,Q; vector<pair<int,int> > q[maxn]; ll ans[maxn];
int find(int x){ return upper_bound(st+1,st+1+top,(node){x+1,0,0})-st-1; }
int main(){
ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
cin>>n>>m;
for(int i=1;i<=m;i++) cin>>c[i];
cin>>Q;
for(int i=1;i<=Q;i++){
int x,y; cin>>x>>y;
if(n==1) cout<<y<<"\n";
q[y].pb(x,i);
}
if(n==1) return 0;
for(int i=1;i<=m;i++){
if(i==1) st[++top]=(node){1,1,-c[1]};
else{
while(top){
int x=st[top].x;
ll v1=st[top].get(x,i);
ll v2=st[top].get(x+1,i);
if(v1+c[i]<v2){ top--; continue; }
if(st[top].get(n-1,i)+c[i]>=st[top].get(n,i)) break;
int l=x+1,r=n-1;
while(l<r){
int mid=(l+r)>>1;
if(st[top].get(mid,i)+c[i]<st[top].get(mid+1,i)) r=mid;
else l=mid+1;
}
v1=st[top].get(l,i);
st[++top]=(node){l,i,v1-1ll*l*c[i]};
break;
}
if(!top) st[++top]=(node){1,i,i-1-c[i]};
}
for(auto z:q[i]) ans[z.se]=st[find(z.fi)].get(z.fi,i);
}
for(int i=1;i<=Q;i++) cout<<ans[i]<<"\n";
return 0;
}