• 【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;
    }
    
  • 相关阅读:
    查看进程的pid和ppid
    多进程《二》开启进程的两种方式
    多进程《一》进程理论
    并发编程《二》操作系统介绍2
    并发编程《一》操作系统介绍1
    Google浏览器历史版和下载地址
    python爬取淘宝排名
    字符编码
    hashlib
    离散数学
  • 原文地址:https://www.cnblogs.com/stoorz/p/14223752.html
Copyright © 2020-2023  润新知