【BZOJ-4435】Juice Junctions 最小割树(分治+最小割)+Hash
4435: [Cerc2015]Juice Junctions
Time Limit: 20 Sec Memory Limit: 512 MBSubmit: 20 Solved: 11
[Submit][Status][Discuss]
Description
你被雇佣升级一个旧果汁加工厂的橙汁运输系统。系统有管道和节点构成。每条管道都是双向的,且每条管道的流量都是1升每秒。管道可能连接节点,每个节点最多可以连接3条管道。节点的流量是无限的。节点用整数1到n来表示。在升级系统之前,你需要对现有系统进行分析。对于两个不同节点s和t,s-t的流量被定义为:当s为源点,t为汇点,从s能流向t的最大流量。以下面的第一组样例数据为例,1-6的流量为3,1-2的流量为2。计算每一对满足a<b的节点a-b的流量的和。
Input
第一行包括2个整数n和m(2<=n<=3000,0<=m<=4500)——节点数和管道数。
接下来m行,每行包括两个相异整数a,b(1<=a,b<=n),表示一条管道连接节点a,b。
每个节点最多连接3条管道,每对节点最多被一条管道连接。
Output
输出一个整数——每对满足a<b的节点a-b的流量之和。
Sample Input
6 8
1 3
2 3
4 1
5 6
2 6
5 1
6 4
5 3
1 3
2 3
4 1
5 6
2 6
5 1
6 4
5 3
Sample Output
36
HINT
Source
Solution
最小割树+Hash
根据最大流-最小割定理,把求最大流转化为求最小割,那么最小割树搞搞
因为每个点的度有限制,所以最小割不能超过3
把最小割hash出来,然后求和即可,大体的hash就是$hash[i][j]$表示最小割为$i$的时候,$j$点在分治过程中是否于$S$连通
PS:据说这题卡Dinic和ISAP的常数,只能用EK,但是好像Dinic能跑过?
UPD:事后和CA爷Claris讨论起来,EK是根据流量的复杂度,常数小,实用于这题;但我说Dinic也能过啊,慢了1倍是真的...然后得知原题时限7s....丧心病狂
Code
#include<iostream> #include<cstdio> #include<algorithm> #include<cstring> #include<cmath> #include<queue> using namespace std; int read() { int x=0,f=1; char ch=getchar(); while (ch<'0' || ch>'9') {if (ch=='-') f=-1; ch=getchar();} while (ch>='0' && ch<='9') {x=x*10+ch-'0'; ch=getchar();} return x*f; } int n,m; #define maxm 10010 #define maxn 3010 struct Edgenode{int next,cap,to;}edge[maxm]; int head[maxn],cnt=1; void add(int u,int v,int w) { cnt++; edge[cnt].to=v; edge[cnt].next=head[u]; head[u]=cnt; edge[cnt].cap=w; } void insert(int u,int v,int w) { add(u,v,w); add(v,u,w); } int dis[maxn],que[maxn<<1],cur[maxn],S,T; bool bfs() { for (int i=1; i<=n; i++) dis[i]=-1; que[0]=S; dis[S]=0; int he=0,ta=1; while (he<ta) { int now=que[he++]; for (int i=head[now]; i; i=edge[i].next) if (edge[i].cap && dis[edge[i].to]==-1) dis[edge[i].to]=dis[now]+1,que[ta++]=edge[i].to; } return dis[T]!=-1; } int dfs(int loc,int low) { if (loc==T) return low; int w,used=0; for (int i=cur[loc]; i; i=edge[i].next) if (edge[i].cap && dis[edge[i].to]==dis[loc]+1) { w=dfs(edge[i].to,min(low-used,edge[i].cap)); edge[i].cap-=w; edge[i^1].cap+=w; used+=w; if (edge[i].cap) cur[loc]=i; if (used==low) return low; } if (!used) dis[loc]=-1; return used; } #define inf 0x7fffffff int dinic() { int tmp=0; while (bfs()) { for (int i=1; i<=n; i++) cur[i]=head[i]; tmp+=dfs(S,inf); } return tmp; } bool visit[maxn]; void DFS(int x) { visit[x]=1; for (int i=head[x]; i; i=edge[i].next) if (!visit[edge[i].to] && edge[i].cap) DFS(edge[i].to); } int id[maxn],tmp[maxn]; unsigned BASE=1,hash[4][maxn]; void work(int L,int R) { if (L==R) return; for (int i=2; i<=cnt; i+=2) edge[i].cap=edge[i^1].cap=(edge[i].cap+edge[i^1].cap)>>1; S=id[L],T=id[R]; int maxflow=dinic(); memset(visit,0,sizeof(visit)); DFS(S); BASE*=131; for (int i=1; i<=n; i++) if (~dis[i]) hash[maxflow][i]+=BASE; int l=L,r=R; for (int i=L; i<=R; i++) if (visit[id[i]]) tmp[l++]=id[i]; else tmp[r--]=id[i]; for (int i=L; i<=R; i++) id[i]=tmp[i]; work(L,l-1); work(r+1,R); } int ans=0; int main() { n=read(),m=read(); for (int u,v,i=1; i<=m; i++) u=read(),v=read(),insert(u,v,1); for (int i=1; i<=n; i++) id[i]=i; work(1,n); for (int i=1; i<=n; i++) for (int j=i+1; j<=n; j++) for (int k=0; k<=3; k++) if (hash[k][i]!=hash[k][j]) {ans+=k;break;} printf("%d\n",ans); return 0; }
被卡常数的教育:(成功垫底.....)
——It's a lonely path. Don't make it any lonelier than it has to be.