P7294 [USACO21JAN] Minimum Cost Paths P 题解
给一种比较无脑的解法。
首先交换题目中
转移有:
考虑经典套路,对
注意到
考虑一行一行地转移,首先
然后对所有
观察此过程,我们发现,
首先令
g_{i,j}=g_{i-1,j}+2j-1 。然后找到第一个大于等于
c_i 的g_{i,j} ,令g_{i,j} \gets c_i(\Delta \gets g_{i,j}-c_i) ,然后g_{i,j+1} \gets \Delta+g_{i,j+1} ,以此类推。最后
g 的形态是前面若干个小于等于c_i 的值,后面全部变成c_i 。
于是,我们再次对
首先令
h_{i,j}=h_{i-1,j}+2 。然后找到第一个
j 满足\sum_{k=1}^j h_{i,k} \ge c_i ,令h_{i,j} = c_i-\sum_{k=1}^{j-1} h_{i,k} ,剩下的h_{i,p} \gets 0(p>j) 。
第一步可以使用区间加操作,第二步可以线段树上二分,最后一步是单点修改。
为了方便起见,线段树的每个节点可以维护两个值
最后查询离线下来,对
时间复杂度
#include<bits/stdc++.h>
#define ll long long
#define N 200005
using namespace std;
struct noede{ll x,y,id;}p[N];
bool cmp(noede a,noede b){return a.x<b.x;}
struct node{ll sum,sum1,cnt,tag;}tr[N<<2];
ll n,m,q,i,j,a[N],ans[N],root,top,f1;
inline void pushup(ll p,ll s,ll t){
tr[p].cnt = tr[2*p].cnt+tr[2*p+1].cnt;
tr[p].sum = tr[2*p].sum+tr[2*p+1].sum;
tr[p].sum1 = tr[2*p+1].sum1+tr[2*p].sum1+tr[2*p].sum*tr[2*p+1].cnt;
}
inline void pushtag(ll p,ll c){tr[p].tag+=c,tr[p].sum+=tr[p].cnt*c,tr[p].sum1+=(1+tr[p].cnt)*tr[p].cnt/2*c;}
inline void pushdown(ll p){pushtag(2*p,tr[p].tag),pushtag(2*p+1,tr[p].tag),tr[p].tag=0;}
inline void add(ll x,ll c1,ll c2,ll s,ll t,ll p){
if(s==t){
tr[p].cnt=c1,tr[p].sum=c1*c2,tr[p].sum1=(1+c1)*c1/2*c2;
return ;
}
if(tr[p].tag) pushdown(p);
if(x<=(s+t)/2) add(x,c1,c2,s,(s+t)/2,2*p);
else add(x,c1,c2,(s+t)/2+1,t,2*p+1);
pushup(p,s,t);
}
inline pair<ll,ll> query(ll cnt,ll s,ll t,ll p){
if(!cnt) return make_pair(0,0);
if(s==t) return make_pair(cnt*(tr[p].sum/tr[p].cnt),(1+cnt)*cnt/2*(tr[p].sum/tr[p].cnt));
if(tr[p].tag) pushdown(p);
if(tr[2*p].cnt<cnt){
pair<ll,ll> ans = query(cnt-tr[2*p].cnt,(s+t)/2+1,t,2*p+1);
ans.first += tr[2*p].sum,ans.second += tr[2*p].sum*(cnt-tr[2*p].cnt)+tr[2*p].sum1;
return ans;
}
else return query(cnt,s,(s+t)/2,2*p);
}
inline pair<ll,ll> querys(ll x,ll s,ll t,ll p){
if(s==t) return make_pair(tr[p].cnt,tr[p].sum/tr[p].cnt);
if(tr[p].tag) pushdown(p);
if(x<=(s+t)/2) return querys(x,s,(s+t)/2,2*p);
else return querys(x,(s+t)/2+1,t,2*p+1);
}
int main(){
scanf("%lld%lld",&n,&m);
for(i=1;i<=m;i++) scanf("%lld",&a[i]);
scanf("%lld",&q);
for(i=1;i<=q;i++) scanf("%lld%lld",&p[i].x,&p[i].y),swap(p[i].x,p[i].y),p[i].id=i;
sort(p+1,p+q+1,cmp);
top=1,add(1,1,a[1],1,2e5,1);
for(i=1,j=1;i<=m;i++){
top++,add(top,1e9,0,1,2e5,1);
for(;j<=q&&p[j].x==i;j++) ans[p[j].id]=f1*p[j].y+query(p[j].y-1,1,2e5,1).second;
if(i!=m){
f1++,pushtag(1,2);
if(f1+querys(1,1,2e5,1).second>=a[i+1]){
while(top) add(top,0,0,1,2e5,1),top--;
top=1,add(1,1,a[i+1]-f1,1,2e5,1);
}
else{
pair<ll,ll> las;
while(f1+tr[1].sum>=a[i+1]&&top>=1){
las = querys(top,1,2e5,1),add(top,0,0,1,2e5,1);
top--;
}
ll delta = (a[i+1]-tr[1].sum-f1),num = (las.second==0?0:delta/las.second),rest = delta-num*las.second;
if(num) top++,add(top,num,las.second,1,2e5,1);
if(rest) top++,add(top,1,rest,1,2e5,1);
}
}
}
for(i=1;i<=q;i++) printf("%lld\n",ans[i]);
return 0;
}