转化题意:
给定 ,一个长 的序列 和一个长 的二元组序列 ,对于每个 ,定义 为 加上 的前 小值的和,求 的最小值。
有 次修改,每次修改完都要输出 的最小值,修改有两种类型:
- 修改某一个 ;
- 修改某一个二元组;
,,输入的所有数是 内的正整数。
考虑线段树,每个节点维护区间内 的和 , 在区间内的前 小的 , 的前缀和 (),区间内 的子问题的答案 。那么 update 的时候有 ,最终答案为 。
注意到 是凸的,故对于任意 ,若 ,则 。故 的决策点 是单调递增的,那么根据决策单调性使用分治优化卷积即可。
时间复杂度 ,代码如下:
#include <iostream>
#include <algorithm>
#include <cstdio>
#include <vector>
#include <set>
using namespace std;
typedef long long ll;
const int S=250005;
const ll inf=1e17;
struct node
{
int op,x,y,z;
};
int n,m,q,k;
int a[S],b[S],c[S],d[S];
node que[S];
int tot,tmp[S*3];
ll sm[S*3<<2];
vector<int> mn[S*3<<2];
vector<ll> pre[S*3<<2],f[S*3<<2];
ll val[S*3];
multiset<int> st[S*3];
void doit(vector<ll> &h,vector<ll> &f,vector<ll> &g,int l,int r,int kl,int kr)
{
if(l>r) return;
int mid=l+r>>1;
int n=g.size()-1;
int kx=-1;
for(int i=max(kl,mid-n);i<=kr&&i<=mid;i++)
{
ll pre=f[i]+g[mid-i];
if(pre<h[mid]) h[mid]=pre,kx=i;
}
doit(h,f,g,l,mid-1,kl,kx),doit(h,f,g,mid+1,r,kx,kr);
}
inline void upda(int u)
{
int ls=u<<1,rs=u<<1|1;
sm[u]=sm[ls]+sm[rs];
{// mn & pre
mn[u].clear();
int lsz=mn[ls].size(),rsz=mn[rs].size();
int i=0,j=0;
while(mn[u].size()<k&&(i<lsz||j<rsz))
{
if(i<lsz&&(j==rsz||mn[ls][i]<mn[rs][j]))
mn[u].push_back(mn[ls][i++]);
else
mn[u].push_back(mn[rs][j++]);
}
int sz=mn[u].size();
pre[u].resize(sz+1);
pre[u][0]=0;
for(int i=1;i<=sz;i++)
pre[u][i]=pre[u][i-1]+mn[u][i-1];
}
int sz=mn[u].size();
f[u].resize(sz+1);
for(int i=0;i<=sz;i++) f[u][i]=inf;
doit(f[u],f[ls],pre[rs],0,sz,0,f[ls].size()-1);
for(int i=0;i<=sz;i++) f[u][i]+=sm[rs];
for(int i=0;i<f[rs].size();i++) f[u][i]=min(f[u][i],f[rs][i]);
}
inline void init(int u,int p)
{
sm[u]=val[p];
int sz=min(k,(int)st[p].size());
mn[u].clear();
for(int x:st[p])
{
mn[u].push_back(x);
if(mn[u].size()==sz) break;
}
pre[u].resize(sz+1);
pre[u][0]=0;
for(int i=1;i<=sz;i++)
pre[u][i]=pre[u][i-1]+mn[u][i-1];
f[u].resize(sz+1);
for(int i=0;i<=sz;i++) f[u][i]=pre[u][i];
}
void build(int u,int l,int r)
{
if(l==r) return init(u,l),void();
int mid=l+r>>1;
build(u<<1,l,mid),build(u<<1|1,mid+1,r);
upda(u);
}
void updp(int u,int l,int r,int p)
{
if(l==r) return init(u,l),void();
int mid=l+r>>1;
if(p<=mid) updp(u<<1,l,mid,p);
else updp(u<<1|1,mid+1,r,p);
upda(u);
}
inline ll getans()
{
ll res=sm[1];
if(f[1].size()==k+1) res=min(res,f[1][k]);
return res;
}
int main()
{
freopen("fire.in","r",stdin);
freopen("fire.out","w",stdout);
ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
cin>>n>>m>>q>>k;
for(int i=1;i<=n;i++) cin>>a[i]>>b[i];
for(int i=1;i<=m;i++) cin>>c[i]>>d[i];
for(int i=1;i<=q;i++) cin>>que[i].op>>que[i].x>>que[i].y>>que[i].z;
for(int i=1;i<=n;i++) tmp[++tot]=b[i];
for(int i=1;i<=m;i++) tmp[++tot]=d[i];
for(int i=1;i<=q;i++) tmp[++tot]=que[i].z;
sort(tmp+1,tmp+tot+1);
tot=unique(tmp+1,tmp+tot+1)-tmp-1;
for(int i=1;i<=n;i++) b[i]=lower_bound(tmp+1,tmp+tot+1,b[i])-tmp;
for(int i=1;i<=m;i++) d[i]=lower_bound(tmp+1,tmp+tot+1,d[i])-tmp;
for(int i=1;i<=q;i++) que[i].z=lower_bound(tmp+1,tmp+tot+1,que[i].z)-tmp;
for(int i=1;i<=n;i++) val[b[i]]+=a[i];
for(int i=1;i<=m;i++) st[d[i]].insert(c[i]);
build(1,1,tot);
for(int i=1;i<=q;i++)
{
int op=que[i].op,x=que[i].x,y=que[i].y,z=que[i].z;
if(op==1)
{
val[b[x]]-=a[x];
updp(1,1,tot,b[x]);
a[x]=y,b[x]=z;
val[b[x]]+=a[x];
updp(1,1,tot,b[x]);
}
else
{
st[d[x]].erase(st[d[x]].find(c[x]));
updp(1,1,tot,d[x]);
c[x]=y,d[x]=z;
st[d[x]].insert(c[x]);
updp(1,1,tot,d[x]);
}
// print(1,1,tot);
cout<<getans()<<'\n';
}
return 0;
}