[BZOJ 3218]a + b Problem

a + b Problem

题解

分块板子题

这道题主要还是建图跑网络流,其实建图的方法还是很常见的。
我们先观察 p i p_{i} pi在什么时候会产生贡献,很明显,如果 a i a_{i} ai [ l i , r i ] [l_{i},r_{i}] [li,ri]中有点为白且这个点选的黑,那它就一定会减去 p i p_{i} pi
换言之,只有全部选黑时它选黑产生的贡献 b i b_{i} bi,否则其贡献就为 b i − p i b_{i}-p_{i} bipi。于是我们就想起了 文理分科,我们可以将向每个点从起点与终点分别连一条为 w i w_{i} wi b i − p i b_{i}-p_{i} bipi流量的边。
对于每个点的组合,向它的每个所属的节点连一条流量为inf的边,再向终点连一条为 p i p_{i} pi的边,表示该组合全选黑时的额外贡献。
根据最大流最小割定理,如果我们跑最大流,可以得到该图的最小割。
由于所有的割边一定时一种合法的宣发,而且得到的就是最小贡献的选法。故与总贡献相减即可得到最大的贡献。

但很明显,这种做法是一定会T的,总的边数可以达到 n 2 n^2 n2,考虑优化。
由于它的组合是向权值在一定范围内的在它前面的数连边,于是我们很容易就想到了分块。听说主席树也可以做,但那样点数会特别多。
我们可以对权值进行分块,对第 i i i个点与它前面的点进行组合的时候,先将它前面的点根据权值分块。
如果一个点的权值为 a j a_{j} aj,就将它连向 a j a_{j} aj所处的块所在的虚点,最后再对在 [ l i , r i ] [l_{i},r_{i}] [li,ri]内的块与点添加与该组合间的流量为inf的边。
由于往后一位会增加一个点入块,我们需要将这个块与点连到一个新点,像可持久化一样。可以发现,总的块点数不会超过 n n n,总边数大概是 2 n n 2n\sqrt{n} 2nn 左右的样子。
注意,上面的分块需要在去重后再分块,不然直接分块的话边数可能会被卡到 n 2 n^2 n2级别。

但这个时候你将代码交上去你会发现你还是T了,而且只T了一个点,是第4个数据点。
这个时候就需要请出我们的当前弧优化了,加上当前弧优化,直接快了2倍,T的那个点甚至快了20倍。别问笔者是怎么知道的
现在你再把代码交上去就可以过了。

总时间复杂度为 O ( n 7 2 ) O\left(n^{\frac{7}{2}}\right) O(n27),虽然看起来过不了但它的确过了,谁都知道网络流的时间复杂度是基本跑不到的嘛。
update:事实上我们的边如果设为 b − p b-p bp的话就会产生负边,而负流量严格来说时不合法的,或许能过 D a r k B z o j DarkBzoj DarkBzoj上的测试点,但事实上会在 U O J UOJ UOJ e x t r a   t e s t extra\,test extratest中挂掉,解决办法是在将我们 b − p b-p bp w w w的边权变为 b b b w + p w+p w+p,这样就没有负流量了。

源码

分块

#include<cstdio>
#include<cmath>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<queue>
using namespace std;
#define MAXN 20005
#define MAXM 1000005
#define reg register
typedef long long LL;
const LL INF=0x7f7f7f7f7f7f;
template<typename _T>
inline void read(_T &x){
	_T f=1;x=0;char s=getchar();
	while('0'>s||'9'<s){if(s=='-')f=-1;s=getchar();}
	while('0'<=s&&s<='9'){x=(x<<3)+(x<<1)+(s^48);s=getchar();}
	x*=f;
}
int n,head[MAXN],tot,dep[MAXN],S,T,cnt,b[MAXN];
int tott,fk[200],block[MAXN],n1,cur[MAXN];
LL ans;queue<int> q;
struct edge{int to,nxt;LL flow;int op;}e[MAXM];
struct ming{int a,l,r,p;}s[MAXN];
inline void addEdge(const int u,const int v,const LL w){e[++tot]=(edge){v,head[u],w};head[u]=tot;}
inline void addedge(const int u,const int v,const LL w){addEdge(u,v,w);e[tot].op=tot+1;addEdge(v,u,0);e[tot].op=tot-1;}
bool bfs(){
	while(!q.empty())q.pop();
	for(reg int i=1;i<=cnt;++i)dep[i]=0,cur[i]=head[i];
	q.push(S);dep[S]=1;
	while(!q.empty()){
		int u=q.front();q.pop();
		for(reg int i=head[u];i;i=e[i].nxt){
			int v=e[i].to;
			if(!dep[v]&&e[i].flow>0){
				q.push(v);dep[v]=dep[u]+1;
				if(v==T)return 1;
			}
		}
	}
	return 0;
}
LL dfs(const int u,LL maxf){
	if(u==T||!maxf)return maxf;int res=0;
	for(int i=cur[u];i;i=e[i].nxt){
		cur[u]=i;int v=e[i].to;if(!e[i].flow||dep[v]!=dep[u]+1)continue;
		LL f=dfs(v,min(1ll*e[i].flow,maxf));if(!f)continue;
		e[i].flow-=f;e[e[i].op].flow+=f;res+=f;maxf-=f;
		if(!maxf)break;
	}
	return res;
}
inline LL dosaka(){
	LL res=0;
	while(bfs())res+=dfs(S,INF);
	return res;
}
signed main(){
	read(n);S=3*n+1;cnt=T=3*n+2;
	for(reg int i=1;i<=n;++i){
		int a,b,w,l,r,p;
		read(a);read(b);read(w);read(l);read(r);read(p);
		addedge(S,i,w+p);addedge(i,T,b);addedge(i+n,T,p);addedge(i,i+n,INF);
		ans+=b+p+w;s[i]=(ming){a,l,r,p};
	}
	for(reg int i=1;i<=n;++i)b[++tott]=s[i].a;b[++tott]=1e9+1;b[++tott]=0;
	sort(b+1,b+tott+1);tott=unique(b+1,b+tott+1)-b-1;n1=tott/70+1;
	for(reg int i=1;i<=n;++i)s[i].a=lower_bound(b+1,b+tott+1,s[i].a)-b;
	for(reg int i=1;i<=tott;++i)block[i]=(i+n1-1)/n1;
	for(reg int i=1;i<=n;++i){
		const int l1=lower_bound(b+1,b+tott+1,s[i].l)-b,al=block[l1]+(b[(block[l1]-1)*n1+1]<s[i].l);
		const int r1=lower_bound(b+1,b+tott+1,s[i].r)-b,ar=block[r1]-(b[block[r1]*n1]>s[i].r);
		for(reg int j=al;j<=ar;++j)if(fk[j])addedge(fk[j],i+n,INF);
		for(reg int j=1;j<i;++j){
			if(b[s[j].a]<s[i].l||b[s[j].a]>s[i].r)continue; 
			if(block[s[j].a]<al||block[s[j].a]>ar)addedge(j,i+n,INF);
		}
		addedge(i,i+2*n,INF);const int id=block[s[i].a];
		if(fk[id])addedge(fk[id],i+2*n,INF);fk[id]=i+2*n;
	}
	ans-=dosaka();printf("%lld\n",ans);
	return 0;
}

附上新打的通过主席树维护的代码,竟然只比分块快一倍。

#include<bits/stdc++.h>
using namespace std;
#define MAXN 5005
#define MAXM 100005
#define lowbit(x) (x&-x)
#define reg register
#define pb push_back
#define mkpr make_pair
#define fir first
#define sec second
typedef long long LL;
typedef unsigned long long uLL;       
const LL INF=0x3f3f3f3f;       
const int mo=1e9+7;
const int inv2=499122177;
const int jzm=2333;
const int zero=10000;
const int orG=3,invG=332748118;
const double Pi=acos(-1.0);
const double eps=1e-5;
typedef pair<LL,int> pii;
template<typename _T>
_T Fabs(_T x){return x<0?-x:x;}
template<typename _T>
void read(_T &x){
	_T f=1;x=0;char s=getchar();
	while(s>'9'||s<'0'){if(s=='-')f=-1;s=getchar();}
	while('0'<=s&&s<='9'){x=(x<<3)+(x<<1)+(s^48);s=getchar();}
	x*=f;
}
template<typename _T>
void print(_T x){if(x<0){x=(~x)+1;putchar('-');}if(x>9)print(x/10);putchar(x%10+'0');}
LL gcd(LL a,LL b){return !b?a:gcd(b,a%b);}
int add(int x,int y,int p){return x+y<p?x+y:x+y-p;}
void Add(int &x,int y,int p){x=add(x,y,p);}
int qkpow(int a,int s,int p){int t=1;while(s){if(s&1LL)t=1ll*a*t%p;a=1ll*a*a%p;s>>=1LL;}return t;}
int n,idx,tot,tott,head[MAXM],cur[MAXM],dep[MAXM],S,T,totd,d[MAXN],root[MAXN],ans;
queue<int>q;
struct node{int lson,rson,id;}tr[MAXM];
struct ming{int a,l,r;LL b,w,p;}p[MAXM];
struct edge{int to,nxt;LL flow;int op;}e[MAXM<<2];
void addEdge(int u,int v,LL f){e[++tott]=(edge){v,head[u],f};head[u]=tott;}
void addedge(int u,int v,LL f){addEdge(u,v,f);e[tott].op=tott+1;addEdge(v,u,0);e[tott].op=tott-1;}
void insert(int &now,int las,int l,int r,int ai,int aw){
	if(l>r||l>ai||r<ai)return ;tr[now=++tot]=tr[las];tr[now].id=++idx;
	addedge(aw,idx,INF);if(las)addedge(tr[las].id,idx,INF);
	if(l==r)return ;int mid=l+r>>1;
	if(ai<=mid)insert(tr[now].lson,tr[las].lson,l,mid,ai,aw);
	if(ai>mid)insert(tr[now].rson,tr[las].rson,mid+1,r,ai,aw);
}
void modify(int rt,int l,int r,int al,int ar,int aw){
	if(l>r||l>ar||r<al||al>ar||!rt)return ;int mid=l+r>>1;
	if(al<=l&&r<=ar){addedge(tr[rt].id,aw,INF);return ;} 
	if(al<=mid)modify(tr[rt].lson,l,mid,al,ar,aw);
	if(ar>mid)modify(tr[rt].rson,mid+1,r,al,ar,aw);
}
bool bfs(){
	for(int i=1;i<=idx;i++)dep[i]=0,cur[i]=head[i];
	while(!q.empty())q.pop();q.push(S);dep[S]=1;
	while(!q.empty()){
		int u=q.front();q.pop();//printf("while %d %d\n",u,dep[u]);
		for(int i=head[u];i;i=e[i].nxt){
			int v=e[i].to;if(dep[v]||e[i].flow<=0)continue;
			//printf("%d link to %d\n",u,v);
			q.push(v);dep[v]=dep[u]+1;if(v==T)return 1;
		}
	}
	return 0;
}
LL dfs(int u,LL maxf){
	if(u==T||!maxf)return maxf;LL res=0;
	for(int i=cur[u];i;i=e[i].nxt){
		cur[u]=i;int v=e[i].to;if(!e[i].flow||dep[v]!=dep[u]+1)continue;
		LL f=dfs(v,min(e[i].flow,maxf));if(!f)continue;
		e[i].flow-=f;e[e[i].op].flow+=f;res+=f;maxf-=f;if(!maxf)break;
	}
	return res;
}
LL sakura(){LL res=0;while(bfs())res+=dfs(S,INF);return res;}
signed main(){
	read(n);idx=n;S=++idx;T=++idx;
	for(int i=1;i<=n;i++)
		read(p[i].a),read(p[i].b),read(p[i].w),
		read(p[i].l),read(p[i].r),read(p[i].p),d[++totd]=p[i].a;
	sort(d+1,d+totd+1);totd=unique(d+1,d+totd+1)-d-1;
	for(int i=1;i<=n;i++)
		ans+=p[i].b+p[i].w+p[i].p,
		p[i].a=lower_bound(d+1,d+totd+1,p[i].a)-d,
		p[i].l=lower_bound(d+1,d+totd+1,p[i].l)-d,
		p[i].r=upper_bound(d+1,d+totd+1,p[i].r)-d-1;
	for(int i=1;i<=n;i++){
		addedge(S,i,p[i].w+p[i].p);addedge(i,T,p[i].b);
		insert(root[i],root[i-1],1,totd,p[i].a,i);
		modify(root[i],1,totd,p[i].l,p[i].r,++idx);addedge(idx,T,p[i].p);
		if(p[i].l>p[i].a||p[i].r<p[i].a)addedge(i,idx,INF);
	}
	ans-=sakura();printf("%lld\n",ans);
	return 0;
}

谢谢!!!

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值