一、题目
二、解法
考虑每次增删格子会有什么影响,快速求 ((x,y)) 之间的最短路就可以解决修改了。
我们把第二种边看成区间 ([l,r]),也就是 (r) 是 (l) 后面第一个满足 (p_r>p_l) 的点,现在考虑用区间去替换原来 (i) 到 (i+1) 的边,可以把一个区间的权值定义成 (b[l]-(a[r-1]-a[l-1])),问题是选出不交的区间使得权值最小。
如果没有什么性质是做不动的,结论是:这些区间不会有交。考虑反证法,设四个端点 (x_1<x_2<x_3<x_4),如果 ([x_1,x_3]) 和 ([x_2,x_4]) 分别构成区间,那么 (p_1>p_2,p_1<p_3,p_2>p_3),矛盾。
知道这个性质以后可以考虑离线,如果 ([l,r]) 比里面记录区间的最优方案的总权值还要小,那么直接替换,因为区间不交所以 ([r,n+1]) 的最短路都会受到这次替换的影响,用树状数组实现即可,时间复杂度 (O(nlog n))
三、总结
如果有两种决策(或多种)的问题可以考虑主一副二的方法,先固定一个然后用另一个来调整最优解。
当发现是一个做不动的问题时,可以主动去寻找结论,比如本题我们寻找关于区间的性质。
#include <cstdio>
#include <vector>
#include <iostream>
#include <set>
using namespace std;
const int M = 200005;
const int inf = 1e9;
#define pii pair<int,int>
#define make make_pair
#define int long long
int read()
{
int x=0,f=1;char c;
while((c=getchar())<'0' || c>'9') {if(c=='-') f=-1;}
while(c>='0' && c<='9') {x=(x<<3)+(x<<1)+(c^48);c=getchar();}
return x*f;
}
int n,m,q,p[M],a[M],b[M],to[M],st[M],id[M];
set<int> s;vector<pii> v[M];int ans[M],bit[M];
int lowbit(int x)
{
return x&(-x);
}
int ask(int x)
{
int r=0;
for(int i=x;i>=1;i-=lowbit(i))
r+=bit[i];
return r;
}
void add(int x,int y)
{
for(int i=x;i<=n+1;i+=lowbit(i))
bit[i]+=y;
}
signed main()
{
n=read();q=read();
for(int i=1;i<=n;i++)
p[i]=read();
for(int i=1;i<=n;i++)
a[i]=read()+a[i-1];//prefix sum
for(int i=1;i<=n;i++)
b[i]=read();
st[++m]=inf;id[m]=n+1;
for(int i=n;i>=1;i--)//maintain a stack
{
int l=1,r=m;
while(l<=r)
{
int mid=(l+r)>>1;
if(st[mid]>p[i])
{
to[i]=id[mid];
l=mid+1;
}
else r=mid-1;
}
while(p[i]>=st[m]) m--;
st[++m]=p[i],id[m]=i;
b[i]-=a[to[i]-1]-a[i-1];//rewrite the value
}
s.insert(1);s.insert(n+1);
v[1].push_back(make(n+1,0));
for(int i=1;i<=q;i++)//off line
{
int x=read();
if(x==1) continue;
if(s.find(x)==s.end())
{
auto it=s.lower_bound(x);
int r=*it;it--;int l=*it;
v[l].push_back(make(r,-i));
v[l].push_back(make(x,i));
v[x].push_back(make(r,i));
s.insert(x);
}
else
{
s.erase(x);
auto it=s.lower_bound(x);
int r=*it;it--;int l=*it;
v[l].push_back(make(r,i));
v[l].push_back(make(x,-i));
v[x].push_back(make(r,-i));
}
}
for(int i=n;i>=1;i--)
{
int x=ask(to[i]);
if(x>b[i]) add(to[i],b[i]-x);
for(auto y:v[i])
{
if(y.second>=0)
ans[y.second]+=ask(y.first)+a[y.first-1]-a[i-1];
else
ans[-y.second]-=ask(y.first)+a[y.first-1]-a[i-1];
}
}
for(int i=1;i<=q;i++)
{
ans[i]+=ans[i-1];
printf("%lld
",ans[i]);
}
}