题目
题目链接: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)。
也就是说对于一个染色方案,好看度为:
现在给你 (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;
}