JZOJ6734. 【2020.06.18省选模拟】航行

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);
}

猜你喜欢

转载自blog.csdn.net/qq_43649416/article/details/106848623