【题目泛做】拆分(网络流)

CF 212A 数据范围放大版。


题解:

首先容易发现每个点的 minmax 差不会超过 1 1 1,且差为 1 1 1 当且仅当度数不是 t t t 的倍数。

换言之,只要保证每个点 i i i 分给 j j j 的都在 ⌊ d e g / t ⌋ \lfloor deg/t\rfloor deg/t ⌈ d e g / t ⌉ \lceil deg/t\rceil deg/t 即可。证明考虑归纳即可。

那么第一个思路就很显然了,考虑归纳,我们先决定哪些边给到 t t t,然后 − − t --t t 考虑下一个问题,显然可以直接网络流。

还有一个好点的思路,考虑二分图边染色,我们把每个点拆成若干个点,加边的同时利用增广保证没有重复颜色即可。


代码1(网络流):

#include<bits/stdc++.h>
#define ll long long
#define re register
#define cs const

using std::cerr;
using std::cout;

cs int N=6e2+7,M=1e6+7;

int deg[N];
int bel[M],rec[M];
int a,b,m,k;

int S,T;
int el[N],nx[M],to[M],cap[M],ec=1;
void adde(int u,int v,int c){
	nx[++ec]=el[u],el[u]=ec,to[ec]=v,cap[ec]=c;
	nx[++ec]=el[v],el[v]=ec,to[ec]=u,cap[ec]=0;
}

int lev[N],gap[N],cur[N],finished;
void BFS(){
	memset(lev,0,sizeof lev);
	memset(gap,0,sizeof gap);
	std::queue<int> q;q.push(T);lev[T]=gap[1]=1;
	while(!q.empty()){
		int u=q.front();q.pop();cur[u]=el[u];
		for(int re e=el[u];e;e=nx[e])
			if(!bel[e]&&!lev[to[e]]){
				lev[to[e]]=lev[u]+1;
				++gap[lev[to[e]]];
				q.push(to[e]);
			}
	}finished=lev[S]==0;
}

int dfs(int u,int flow){
	if(u==T)return flow;int ans=0;
	for(int &e=cur[u];e;e=nx[e])
		if(!bel[e]&&cap[e]&&lev[to[e]]+1==lev[u]){
			int dlt=dfs(to[e],std::min(flow-ans,cap[e]));
			cap[e]-=dlt;cap[e^1]+=dlt;
			if((ans+=dlt)==flow)return ans;
		}
	if(!--gap[lev[u]])finished=true;
	++gap[++lev[u]];cur[u]=el[u];
	return ans;
}

int vs[N],idx;

bool fix_flow(int u){
	if(vs[u]==idx)return false;
	vs[u]=idx;int eu=u+m;
	if(cap[eu<<1|1]>deg[u]/k){
		--cap[eu<<1|1];
		++cap[eu<<1];
		return true;
	}
	for(int re e=el[u];e;e=nx[e])
		if(!bel[e]&&rec[e]==cap[e]&&vs[to[e]]!=idx){
			vs[to[e]]=idx;int v=to[e];
			for(int re r=el[v];r;r=nx[r])
				if(!bel[r]&&rec[r]!=cap[r]&&fix_flow(to[r])){
					int w=cap[r]<rec[r]?-1:1;
					cap[r]-=w,cap[r^1]+=w;
					cap[e]-=w,cap[e^1]+=w;
					return true;
				}
		}
	return false;
}

void Flow(){
	BFS();while(!finished)dfs(S,1e9);
	for(int re i=1;i<=a+b;++i)
		while(cap[(i+m)<<1|1]<deg[i]/k){
			vs[S]=vs[T]=++idx;
			assert(fix_flow(i));
			++cap[(i+m)<<1|1];
			--cap[(i+m)<<1];
		}
}

void Main(){
	scanf("%d%d%d%d",&a,&b,&m,&k);
	S=0,T=a+b+1;
	for(int re i=1;i<=m;++i){
		int u,v;scanf("%d%d",&u,&v);
		++deg[u],++deg[v+a];adde(u,v+a,1);
	}
	for(int re i=1;i<=a;++i)
		adde(S,i,0);
	for(int re j=1;j<=b;++j)
		adde(j+a,T,0);
	for(;k;--k){
		for(int re i=1;i<=a+b;++i)
			cap[(i+m)<<1]=(deg[i]+k-1)/k,
			cap[(i+m)<<1|1]=0;
		for(int re i=2;i<=ec;++i)
			rec[i]=cap[i];
		Flow();
		for(int re i=2;i<=m+m+1;++i)
			if(rec[i]!=cap[i]){
				--deg[to[i]];
				bel[i]=k;
			}
	}
	for(int re i=1;i<=m;++i)
		cout<<bel[i<<1]<<" ";
}

inline void file(){
#ifdef zxyoi
	freopen("divide.in","r",stdin);
#endif
	int size=40<<20;//40M
    __asm__ ("movq %0,%%rsp\n"::"r"((char*)malloc(size)+size));//提交用这个  
}signed main(){file();Main();exit(0);}

代码2(二分图边染色):

#include<bits/stdc++.h>
#define ll long long
#define re register
#define cs const

using std::cerr;
using std::cout;

cs int N=3e2+7,M=1e5+7;

int c0,c1;
int nx[2][N+N][N];
void dfs(int o,int x,int y,int cx,int cy){
	int to=nx[o^1][y][cx];
	nx[o][x][cx]=y;
	nx[o^1][y][cx]=x;
	if(!to){
		nx[o^1][y][cy]=0;
		return;
	}dfs(o^1,y,to,cy,cx);
}
int a,b,m,k,ans[M];
int sz[2][N],id[2][N];
int eid[N+N][N+N];

void Main(){
	scanf("%d%d%d%d",&a,&b,&m,&k);
	std::fill(sz[0]+1,sz[0]+a+1,k);
	std::fill(sz[1]+1,sz[1]+b+1,k);
	for(int re i=1;i<=m;++i){
		int u,v;scanf("%d%d",&u,&v);
		if(sz[0][u]==k)
			sz[0][u]=0,id[0][u]=++c0;
		if(sz[1][v]==k)
			sz[1][v]=0,id[1][v]=++c1;
		++sz[0][u],u=id[0][u];
		++sz[1][v],v=id[1][v];
		eid[u][v]=i;int cu=1,cv=1;
		for(;nx[0][u][cu];++cu);
		for(;nx[1][v][cv];++cv);
		if(cu==cv){
			nx[0][u][cu]=v;
			nx[1][v][cv]=u;
		}else dfs(0,u,v,cu,cv);
	}
	for(int re i=1;i<=c0;++i)
		for(int re c=1;c<=k;++c)
			if(nx[0][i][c])ans[eid[i][nx[0][i][c]]]=c;
	for(int re i=1;i<=m;++i)
		cout<<ans[i]<<" ";
}

inline void file(){
#ifdef zxyoi
	freopen("divide.in","r",stdin);
#endif
}signed main(){file();Main();return 0;}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值