BZOJ#3218. a + b Problem——Minimum cut of network flow + tree construction

topic

Dark explosion P3218

answer

When you see the title that says using network streaming to do A+B, your first reaction: This is a spoof blog

But this question is not A+B, it is a+b! (Case sensitive) , you will find something is wrong when you look at the title...

To get into the topic, we can first transform the problem:

First p_isubtract everything , if the point is white, or if the point and the previous qualified points are all black, add it p_i.

At this time, if you come up with a point S for white and T for black, take each point in the array on the graph, and define the point connected with S to be a white point, and the point connected to T with an edge is black,

This problem can be converted into a minimum cut problem.

The capacity of the edge connected from S is, and the capacity of the edge w_i+p_iconnected to T is b_i(note the direction),

Then how to deal with this condition that requires a bunch of dots to be black?

You can create a virtual point (correspondingly, the point in the array corresponds to the point on the graph is a real point), and there is p_ian edge with a capacity of connected to T, because according to requirements, if the edge is not cut, the virtual point is smoothly connected to T , Then the corresponding real points that must be black points must also have edges connected to T, so an edge connected virtual point with capacity INF can be established from each point. Because the capacity is a maximum value, this kind of edge cannot be included in the minimum cut and cannot be cut.

So far we ran a maximum flow Dinic to find the minimum cut, and then subtracted it from the sum of the edge capacity (except the INF edge), and it was solved soon?

However, the scope of this question is very unfriendly. n \ leq 5000According to the above mapping method, although the number of points is the O (n)level, the number of sides O (n ^ 2);

The space is also not friendly, each test point is only 48MB, so I had to open int carefully. .

Therefore, we consider to reduce the number of edges by creating more virtual points. There are many specific methods, and there is more than one optical partition.

I will only introduce a very safe way to build a virtual chairman tree.

Specifically, after discretizing all the values ​​of a, l, and r, a_ja weighted line segment tree is established for all the prefixes (1≤j≤i) B_i, and each interval on the line segment tree represents one in the figure. Imaginary point,

We define this as the dotted dotted interval (for convenience, the previously established with a capacity of p_iedges connected to point T is the imaginary dotted conditions), then the tree B_ion the interval (l, r) corresponding to the dotted section must It satisfies that all 1 \ leq j \ leq i, l \ leq a_j \ leq rj corresponds to the real point on the graph directly or indirectly connected to the INF edge via other imaginary points in the interval.

This sentence is a bit long, but it can definitely be understood. In order to meet this condition, it is only necessary to connect the imaginary point corresponding to the interval between the left son and the right son to it, and the real point that satisfies the condition is connected to the leaf node,

Because  a\overset{inf}{\rightarrow} b and  a\overset{inf}{\rightarrow} c(Virtual point) \overset{inf}{\rightarrow} b are equivalent, so every tree B_{i-1}found (on l_i,r_i) corresponding to a plurality of virtual point range, in place of the original virtual point corresponding to the condition to be connected to a number of our established solid dot black dots, corresponding to a maximum because there is log_2na Interval, at most new log_2nedges,

Then, for each tree B, we use the chairman tree to dynamically open points and create at most log_2none point and 2log_2nedge each time , so the total number of points on the graph does not exceed nlog_2n\cdot C, the total number of edges does not exceed nlog_2n\cdot D(C and D are constants <10), and the space is not It will explode, and Dinic just happens to be able to run it.

Code

Added a few unnecessary optimizations, just understand it

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<cmath>
#include<vector>
#include<queue>
#include<climits>
#include<map>
#define ll long long
#define MAXN 400005    //边数点数大概是这个数量级
#define mst0(x) memset(x,0,sizeof(x))
#define INF 0x3f3f3f3f
using namespace std;
inline ll read(){
	ll x=0;bool f=1;char s=getchar();
	while((s<'0'||s>'9')&&s>0){if(s=='-')f^=1;s=getchar();}
	while(s>='0'&&s<='9')x=(x<<1)+(x<<3)+s-'0',s=getchar();
	return f?x:-x;
}
int n,IN,N;
int in[5005][6],SUM;
map<int,int>mp;
int root[MAXN],NN,XP,R;

int f[MAXN];
int d[MAXN],cur[MAXN];
struct edge{
	int v,id;edge(){}
	edge(int V,int I){v=V,id=I;}
};
vector<edge>G[MAXN];
queue<int>q;
bool ct[MAXN];
inline bool bfs(int S,int T){
	for(int i=0,lm=min(XP,MAXN-5);i<=lm;i++)d[i]=-1;
	q.push(S),d[S]=0;
	while(!q.empty()){
		int u=q.front();q.pop();
		for(int i=0;i<G[u].size();i++){
			int v=G[u][i].v,a=G[u][i].id;
			if(ct[v]&&f[a]>0&&d[v]<0)d[v]=d[u]+1,q.push(v);
		}
	}
	return d[T]>=0;
}
inline int dfs(int x,int lim,int T){
	if(x==T)return lim;
	int res=lim;
	for(int i=cur[x];i<G[x].size()&&res>0;i++){
		cur[x]=i;    //当前弧优化
		int v=G[x][i].v,a=G[x][i].id;
		if(ct[v]&&f[a]>0&&d[v]==d[x]+1){
			int ad=dfs(v,min(res,f[a]),T);
			f[a]-=ad,f[a^1]+=ad,res-=ad;
		}
	}
	if(lim==res)d[x]=-1;    //吔屎优化
	return lim-res;
}
inline int dinic(int S,int T){
	int res=0;
	while(bfs(S,T)){
		for(int i=0,lm=min(XP,MAXN-5);i<=lm;i++)cur[i]=0;
		while(int ad=dfs(S,INF,T))res+=ad;
	}
	return res;
}
inline void addedge(int u,int v,int w){
	if(w<INF)SUM+=w;
	f[IN]=w,f[IN^1]=0;
	G[u].push_back(edge(v,IN));
	G[v].push_back(edge(u,IN^1));
	IN+=2;
}

struct itn{
	int ls,rs,id;
}t[MAXN];
inline void add(int x,int y,int l,int r,int u){
	if(y>0)addedge(t[y].id,t[x].id,INF);    //把先前点y的信息通过连边转过来
	if(l==r){addedge(u,t[x].id,INF);return;}
	int mid=(l+r)>>1;
	if(in[u][0]<=mid){
		t[x].rs=t[y].rs,t[x].ls=++NN,t[t[x].ls].id=++XP;//另一儿子无需连边,因为先前点y的信息已经转过来了
		add(t[x].ls,t[y].ls,l,mid,u);
		addedge(t[t[x].ls].id,t[x].id,INF);
	}
	else{
		t[x].ls=t[y].ls,t[x].rs=++NN,t[t[x].rs].id=++XP;
		add(t[x].rs,t[y].rs,mid+1,r,u);
		addedge(t[t[x].rs].id,t[x].id,INF);
	}
}
inline void sch(int x,int l,int r,int a,int b,int p){
	if(x==0)return;
	if(l==a&&r==b){addedge(t[x].id,p,INF);return;}
	int mid=(l+r)>>1;
	if(a<=mid)sch(t[x].ls,l,mid,a,min(mid,b),p);
	if(b>mid)sch(t[x].rs,mid+1,r,max(a,mid+1),b,p);
}
inline void DFS(int x){
	if(ct[x])return;ct[x]=1;
	for(int i=0;i<G[x].size();i++)
		if(G[x][i].id&1)DFS(G[x][i].v);
}

int main()
{
	n=read(),N=(n<<1)+1;
	for(int i=1;i<=n;i++){
		for(int j=0;j<6;j++)in[i][j]=read();
		SUM-=in[i][5];
		mp[in[i][0]]=1,mp[in[i][3]]=1,mp[in[i][4]]=1;
	}
	map<int,int>::iterator it;
	for(it=mp.begin();it!=mp.end();it++)it->second=++R;
	for(int i=1;i<=n;i++)
		in[i][0]=mp[in[i][0]],in[i][3]=mp[in[i][3]],in[i][4]=mp[in[i][4]];
	XP=N;
	for(int i=1;i<=n;i++){
		addedge(0,i,in[i][2]+in[i][5]);
		if(i==1)addedge(i,N,in[i][1]+in[i][5]);
		else addedge(i,N,in[i][1]),
		addedge(n+i,N,in[i][5]),addedge(i,n+i,INF);
		sch(root[i-1],1,R,in[i][3],in[i][4],n+i);
		if(i<n){
			root[i]=++NN,t[root[i]].id=++XP;
			add(root[i],root[i-1],1,R,i);
		}
	}
	DFS(N);    //把最终无法通向T的点删去,免跑
	printf("%d\n",SUM-dinic(0,N));
	return 0;
}

 

Guess you like

Origin blog.csdn.net/weixin_43960287/article/details/113704427
Recommended