【UOJ#77】A+B Problem

题目

题目链接:https://uoj.ac/problem/77
题目名称是吸引你点进来的。
从前有个 (n) 个方格排成一行,从左至右依此编号为 (1, 2, cdots, n)
有一天思考熊想给这 (n) 个方格染上黑白两色。
(i) 个方格上有 (6) 个属性:(a_i, b_i, w_i, l_i, r_i, p_i)
如果方格 (i) 染成黑色就会获得 (b_i) 的好看度。
如果方格 (i) 染成白色就会获得 (w_i) 的好看度。
但是太多了黑色就不好看了。如果方格 (i) 是黑色,并且存在一个 (j) 使得 (1 leq j < i)(l_i leq a_j leq r_i) 且方格 (j) 为白色,那么方格 (i) 就被称为奇怪的方格。
如果方格 (i) 是奇怪的方格,就会使总好看度减少 (p_i)
也就是说对于一个染色方案,好看度为:

[sum_{ ext{方格}i ext{为黑色}}{b_i} + sum_{ ext{方格}i ext{为白色}}{w_i} - sum_{ ext{方格}i ext{为奇怪的方格}}{p_i} ]

现在给你 (n, a, b, w, l, r, p),问所有染色方案中最大的好看度是多少。
(nleq 5000,a,l,rleq 10^9,vleq 2 imes 10^5,pleq 3 imes 10^5)

思路

经典题。不过感觉十分缝合。
考虑没有黑白格子之间的限制怎么做。由于答案等价于所有格子黑白价值之和减去不选的颜色的价值之和,可以考虑网络流,源点连向每一个格子连一条流量为 (b_i) 的边,每一个格子向汇点连一条流量为 (w_i) 的边,然后跑最小割即可。
话说为什么不直接在黑格子和白格子权值中取个最大值啊。
现在有了黑白格子之间的限制,就把每一个点拆成两个点,第一个点依然先按上述方式连边,然后每一个点的一号点向二号点连一条流量为 (p_i) 的边。二号点再向所有 (j<i)(l_ileq a_jleq r_i) 的格子的一号点连边,边权为 (+infty)
这样的话,一个格子要么切断黑边(选择白色);要么切断白边和一二号点之间的边(黑色,奇怪的方格);要么切断白边和所有二号点连向的点的白边(黑色,所影响的格子均为黑色)。显然是满足要求的。
但是这样建边的话,边数显然是 (O(n^2)) 的。由于每一个点是向一个区间的点连边,所以上线段树优化建图即可。
但是还有一个要求是每一个二号点只能向编号小于自己的点连边,所以还要套上一个可持久化。最终主席树 + 最小割即可。
时间复杂度 (O(n^3log n)),空间复杂度 (O(nlog n))

代码

人傻常熟巨大,交了三发只过了一发。。。

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;

const int N=120010,Inf=1e9;
const ll InfLL=7e18;
int head[N],rt[N],a[N],b[N],w[N],p[N],L[N],R[N],cur[N],dep[N],pre[N];
int n,S,T,tot=1;
ll maxf,sumf,c[N];

struct edge
{
	int next,to;
	ll flow;
}e[N*20];

void add(int from,int to,ll flow)
{
	e[++tot]=(edge){head[from],to,flow};
	head[from]=tot;
	e[++tot]=(edge){head[to],from,0};
	head[to]=tot;
}

struct SegTree
{
	int tot,lc[N*4],rc[N*4];
	
	int build(int l,int r)
	{
		int x=++tot;
		if (l==r) return x;
		int mid=(l+r)>>1;
		lc[x]=build(l,mid); rc[x]=build(mid+1,r);
		add(x+2*n,lc[x]+2*n,InfLL); add(x+2*n,rc[x]+2*n,InfLL);
		return x;
	}
	
	void adde(int x,int l,int r,int ql,int qr,int u)
	{
		if (ql<=l && r<=qr)
		{
			add(u+n,x+2*n,InfLL);
			return;
		}
		int mid=(l+r)>>1;
		if (ql<=mid) adde(lc[x],l,mid,ql,qr,u);
		if (qr>mid) adde(rc[x],mid+1,r,ql,qr,u);
	}
	
	int update(int now,int l,int r,int k,int u)
	{
		int x=++tot;
		lc[x]=lc[now]; rc[x]=rc[now];
		if (l==k && r==k)
		{
			add(x+2*n,u,InfLL);
			return x;
		}
		int mid=(l+r)>>1;
		if (k<=mid) lc[x]=update(lc[now],l,mid,k,u);
			else rc[x]=update(rc[now],mid+1,r,k,u);
		add(x+2*n,lc[x]+2*n,InfLL); add(x+2*n,rc[x]+2*n,InfLL);
		return x;
	}
}seg;

bool bfs()
{
	memcpy(cur,head,sizeof(head));
	memset(dep,0x3f3f3f3f,sizeof(dep));
	queue<int> q;
	q.push(S); dep[S]=0;
	while (q.size())
	{
		int u=q.front(); q.pop();
		for (int i=head[u];~i;i=e[i].next)
		{
			int v=e[i].to;
			if (e[i].flow && dep[v]>dep[u]+1)
			{
				dep[v]=dep[u]+1; pre[u]=v;
				q.push(v);
			}
		}
	}
	return dep[T]<Inf;
}

ll dfs(int x,ll flow)
{
	if (x==T) return flow;
	ll used=0,res;
	for (int i=cur[x];~i;i=e[i].next)
	{
		int v=e[i].to; cur[x]=i;
		if (e[i].flow && dep[v]==dep[x]+1)
		{
			res=dfs(v,min(e[i].flow,flow-used));
			used+=res;
			e[i].flow-=res; e[i^1].flow+=res;
			if (used==flow) return used;
		}
	}
	return used;
}

void dinic()
{
	while (bfs())
		maxf+=dfs(S,InfLL);
}

int main()
{
	memset(head,-1,sizeof(head));
	S=N-1; T=N-2;
	scanf("%d",&n);
	for (int i=1;i<=n;i++)
	{
		scanf("%d%d%d%d%d%d",&a[i],&b[i],&w[i],&L[i],&R[i],&p[i]);
		add(S,i,b[i]); add(i,T,w[i]); add(i,i+n,p[i]);
		sumf+=b[i]+w[i]; c[i]=1LL*a[i]*Inf+i;
	}
	sort(c+1,c+1+n);
	int cnt=unique(c+1,c+1+n)-c-1;
	c[++cnt]=InfLL;
	rt[0]=seg.build(1,n);
	for (int i=1;i<=n;i++)
	{
		a[i]=lower_bound(c+1,c+1+cnt,1LL*Inf*a[i]+i)-c;
		L[i]=lower_bound(c+1,c+1+cnt,1LL*Inf*L[i])-c;
		R[i]=lower_bound(c+1,c+1+cnt,1LL*Inf*(R[i]+1))-c-1;
		if (L[i]<=R[i]) seg.adde(rt[i-1],1,n,L[i],R[i],i);
		rt[i]=seg.update(rt[i-1],1,n,a[i],i);
	}
	dinic();
	printf("%lld
",sumf-maxf);
	return 0;
}
原文地址:https://www.cnblogs.com/stoorz/p/14223752.html