Description
n , m , a i < = 1 e 5 n,m,a_i<=1e5 n,m,ai<=1e5
Solution
- 相当神仙的方法。
- 首先考虑一下部分分,如果 a i a_i ai为1,2怎么做。直接就是一个裸的最大权闭合子图,可以线段树优化连边。
- 最大权闭合子图:在有向图中选择一个子图,每一个点被选择会有一个贡献(可负)使得子图中的每一个点的所有后继同时也在子图中。
- 做法考虑网络流:对于所有原点连向每一个正权点,流量为权值,每一个负权点连向汇点,流量为权值的相反数。原图中的边全部为inf。
割掉正权点对应边即为选择正权点,割掉负权点对应边则不选择负权点。那么如果要选择一个正权点,它的所有后继中的负权点都必须选择。答案即为正权值-最小割。
- 实际上我们还可以DP。
- 假设 a i a_i ai在 [ 0 , 1 ] [0,1] [0,1]设 f [ i ] [ 0 / 1 ] f[i][0/1] f[i][0/1]表示 i i i位置选择0/1的最小代价。由于原图中的影响变成了——如果一个点选择0/1,那么它周围一个区间内的所有点必须选择0/1。按照一段0,一段1来转移。
- 考虑 f [ i ] [ 0 ] f[i][0] f[i][0](另一个类似),那么转移就是枚举 j j j,从 f [ j ] [ 1 ] f[j][1] f[j][1]转移过来,中间的贡献可以前缀和计算。
- 考虑选取 j j j的范围:
- 对于任意 k ∈ [ j + 1 , i ] k \in [j+1,i] k∈[j+1,i],需要 L [ k ] [ 0 ] > j , R [ k ] [ 0 ] < i + 1 L[k][0]>j,R[k][0]<i+1 L[k][0]>j,R[k][0]<i+1。
- 相当于 [ j + 1 , i ] [j+1,i] [j+1,i]这段区间是闭合的。
- 前后都可以用单调栈来维护,在后一个栈上二分出最小的位置,因为前一个栈要求能够转移过来的 j j j是栈上不连续的几个位置,用线段树维护和查询即可。
- 可以做到 O ( n l o g n ) O(n\ log\ n) O(n log n)
- 接下来需要扩展到 a i a_i ai为任意值的情况。考虑我们二分一个 m i d mid mid,所有 a i a_i ai要么变成 m i d mid mid,要么变成 m i d + 1 mid+1 mid+1,这相当于上面选 1 , 2 1,2 1,2的问题。
- 重要结论是如果选择 m i d mid mid,那么最后的结果一定 < = m i d <=mid <=mid,否则 > m i d >mid >mid。
- 这样把序列分成互不相关的子问题即可。
- 加上之前的DP可以做到 O ( n l o g 2 n ) O(n\ log^2\ n) O(n log2 n)
#include<cstdio>
#include<cmath>
#include<cstring>
#include<algorithm>
#define maxn 100005
#define ll long long
using namespace std;
int n,m,i,j,k,a[maxn],L0[maxn][2],R0[maxn][2];
int Abs(int x){
return (x<0)?-x:x;}
void read(int &x){
x=0; char ch=getchar();
for(;ch<'0'||ch>'9';ch=getchar());
for(;ch>='0'&&ch<='9';ch=getchar()) x=x*10+ch-'0';
}
int L[maxn][2],R[maxn][2],A[maxn],P[maxn],bz[maxn];
int g[maxn][2],w[2],d[2][maxn],W[2],D[2][maxn];
int v[2],p[maxn],ans0[maxn];
ll ans,sum[2][maxn],f[maxn][2];
int t[2][maxn*4];
void maketree(int x,int l,int r){
t[0][x]=t[1][x]=-1;
if (l==r) return;
int mid=(l+r)>>1;
maketree(x<<1,l,mid),maketree(x<<1^1,mid+1,r);
}
void upd(int k,int x){
int l=t[k][x<<1],r=t[k][x<<1^1];
if (l==-1) t[k][x]=r; else
if (r==-1) t[k][x]=l; else {
if (f[l][k]-sum[k^1][l]<f[r][k]-sum[k^1][r])
t[k][x]=l;
else t[k][x]=r;
}
}
void change(int k,int x,int l,int r,int i,int tp){
if (l==r){
if (tp) t[k][x]=l;
else t[k][x]=-1;
return;
}
int mid=(l+r)>>1;
if (i<=mid) change(k,x<<1,l,mid,i,tp);
else change(k,x<<1^1,mid+1,r,i,tp);
upd(k,x);
}
int find(int k,int x,int l,int r,int L,int R){
if (r<L||l>R) return -1;
if (L<=l&&r<=R) return t[k][x];
int mid=(l+r)>>1;
int id1=find(k,x<<1,l,mid,L,R);
int id2=find(k,x<<1^1,mid+1,r,L,R);
if (id1==-1) return id2;
if (id2==-1) return id1;
return (f[id1][k]-sum[k^1][id1]<f[id2][k]-sum[k^1][id2])?id1:id2;
}
void merge(int st,int ed,int nowl,int nowr){
if (st==ed) {
if (a[p[st]]<nowl) ans+=nowl-a[p[st]],ans0[p[st]]=nowl; else
if (a[p[st]]>nowr) ans+=a[p[st]]-nowr,ans0[p[st]]=nowr; else
ans0[p[st]]=a[p[st]];
return;
}
if (st>ed) return;
if (nowl==nowr) {
for(i=st;i<=ed;i++)
ans+=Abs(nowl-a[p[i]]),ans0[p[i]]=nowl;
return;
}
v[0]=(nowl+nowr)>>1,v[1]=v[0]+1;
int len=ed-st+1;
for(i=1;i<=len;i++) {
P[i]=p[st+i-1],A[i]=a[P[i]];
memcpy(L[i],L0[P[i]],sizeof(L[i]));
memcpy(R[i],R0[P[i]],sizeof(R[i]));
}
for(i=1;i<=len;i++) for(k=0;k<2;k++)
sum[k][i]=sum[k][i-1]+Abs(v[k]-A[i]);
f[0][0]=f[0][1]=g[0][0]=g[0][1]=0;
maketree(1,0,len);
change(0,1,0,len,0,1);
change(1,1,0,len,0,1);
w[0]=w[1]=1,d[0][1]=d[1][1]=0;
W[0]=W[1]=0;
for(i=1;i<=len;i++) {
for(k=0;k<2;k++){
while (W[k]&&R[D[k][W[k]]][k]<=R[i][k]) W[k]--;
D[k][++W[k]]=i;
int l=1,r=W[k],mid,id=0;
while (l<=r){
mid=(l+r)>>1;
if (i<len&&R[D[k][mid]][k]>=P[i+1])
l=mid+1,id=D[k][mid];
else r=mid-1;
}
while (L[i][k]<=P[d[k][w[k]]]) {
change(k^1,1,0,len,d[k][w[k]],0);
w[k]--;
}
g[i][k]=find(k^1,1,0,len,id,i-1);
if (g[i][k]>=0){
f[i][k]=f[g[i][k]][k^1]+sum[k][i]-sum[k][g[i][k]];
change(k,1,0,len,i,1);
}
}
for(k=0;k<2;k++) if (g[i][k]>=0)
d[k^1][++w[k^1]]=i;
}
if (f[len][0]<f[len][1]) k=0; else k=1;
int now=len;
while (now) {
for(i=now;i>g[now][k];i--) bz[i]=k;
now=g[now][k],k^=1;
}
int cnt=0;
for(i=1;i<=len;i++) if (!bz[i])
p[st+cnt]=P[i],cnt++;
int cnt0=cnt;
for(i=1;i<=len;i++) if (bz[i])
p[st+cnt]=P[i],cnt++;
merge(st,st+cnt0-1,nowl,(nowl+nowr)>>1);
merge(st+cnt0,st+cnt-1,((nowl+nowr)>>1)+1,nowr);
}
int main(){
freopen("ceshi.in","r",stdin);
// freopen("sailing.in","r",stdin);
// freopen("sailing.out","w",stdout);
read(n),read(m); int maxa=0;
for(i=1;i<=n;i++) read(a[i]),maxa=max(maxa,a[i]);
for(i=1;i<=n;i++) L0[i][0]=R0[i][0]=L0[i][1]=R0[i][1]=i;
for(i=1;i<=m;i++){
int op,l,r;
read(op),read(l),read(r),read(k);
L0[k][op^1]=min(L0[k][op^1],l);
R0[k][op^1]=max(R0[k][op^1],r);
}
for(i=1;i<=n;i++) p[i]=i;
merge(1,n,1,maxa);
printf("%lld",ans);
}