网络流套路小结
讲课的时候发现自己好像网络流好像还行。。。
但是建模套路还是很多 我这种脑子是不可能记住的 所以写一个总结吧
需要掌握/实现/没写过的标出来了
最小割建模:
最大权闭合子图 CEOI order 线性代数
最大密度子图
文理分科模型(解方程法) BZOJ2132
距离限制模型 切糕
平面图最小割转对偶图最短路 狼抓兔子 海拔
01变量建模 COCONUTS
最小割树(Gomory_Hu树)ZJOI2011最小割
杂题 CC RIN SRM577 BoardPainting SRM558 SurroundingGame
最大流/费用流建模:
环覆盖
HALL定理 CF 103E
不等式差分模型 志愿者招募 Delight For A Cat
上下界网络流 (见liu_runda博客)BZOJ 3698 营救皮卡丘
数据结构优化建图 BZOJ3218
杂 CF 510E 星际穿越 WC 剪刀石头布 BRIDGES(带权混合图欧拉回路) CC GNUM SRM594 FoxAndGo3 SRM627 LaserTowers 美食节 SRM590 FoxAndCity
然后就发现没写的太多了 不标了
一共是24道题 目前来说没有时间做 网络流按照一天4道的话是一周时间 大概在培训结束先开网络流吧
开始填坑了...QAQ
bzoj3996 TJOI2015 线性代数
想了一下想到了Coconuts那个题,然后发现还不是很会统计... 研究了一下洛谷的第一篇题解...发现是神仙的解方程法...学习到了... 就是肯定是最小割建图,考虑建Coconuts那个题的边,然后通过解方程就可以分配每条边了。具体可以参考TA的题解... 后来发现...特么建N*N个点也都能过...我太难了... //Love and Freedom. #include<cstdio> #include<algorithm> #include<cstring> #include<cmath> #include<queue> #define ll long long #define inf 20021225 #define N 501 using namespace std; int read() { int s=0,f=1; char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-') f=-1; ch=getchar();} while(ch>='0'&&ch<='9') s=s*10+ch-'0',ch=getchar(); return f*s; } struct edge{int to,lt,f;}e[N*N*20]; queue<int> q; int dep[N*4],cnt=1,in[N*4],s,t; int a[N][N],c[N]; void add(int x,int y,int f) { e[++cnt].to=y; e[cnt].lt=in[x]; e[cnt].f=f; in[x]=cnt; e[++cnt].to=x; e[cnt].lt=in[y]; e[cnt].f=0; in[y]=cnt; } bool bfs() { while(!q.empty()) q.pop(); memset(dep,0,sizeof(dep)); q.push(s); dep[s]=1; while(!q.empty()) { int x=q.front(); q.pop(); for(int i=in[x];i;i=e[i].lt) if(e[i].f&&!dep[e[i].to]) { q.push(e[i].to),dep[e[i].to]=dep[x]+1; if(e[i].to==t) return 1; } } return 0; } int dfs(int x,int flow) { if(x==t) return flow; int cur=flow; for(int i=in[x];i;i=e[i].lt) if(e[i].f&&dep[e[i].to]==dep[x]+1) { int tmp=dfs(e[i].to,min(e[i].f,cur)); e[i].f-=tmp; e[i^1].f+=tmp; cur-=tmp; if(!cur) return flow; } dep[x]=-1; return flow-cur; } int dinic() { int ans=0; while(bfs()) ans+=dfs(s,inf); return ans; } int main() { int n=read(),ful=0; for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) a[i][j]=read(),ful+=a[i][j]; for(int i=1;i<=n;i++) c[i]=read(); s=N*4-3; t=s+1; for(int i=1;i<=n;i++) { int ans=0; for(int j=1;j<=n;j++) { if(i!=j) add(i,j,a[i][j]+a[j][i]); ans+=a[i][j]+a[j][i]; } add(s,i,ans); add(i,t,c[i]<<1); } printf("%d\n",ful-(dinic()>>1)); return 0; }
bzoj3144 HNOI2013 切糕
经典的距离限制最小割模型 比较有趣。 (x,d)->(y,d-k) (y,d)->(x,d-k) inf 可以限制两个点之间距离不超过k //Love and Freedom. #include<cstdio> #include<algorithm> #include<cstring> #include<cmath> #include<queue> #define ll long long #define inf 20021225 #define N 200010 using namespace std; int read() { int s=0,f=1; char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-') f=-1; ch=getchar();} while(ch>='0'&&ch<='9') s=s*10+ch-'0',ch=getchar(); return f*s; } struct edge{int to,lt,f;}e[N*40]; queue<int> que; int dep[N],cnt=1,in[N],s,t; void add(int x,int y,int f) { e[++cnt].to=y; e[cnt].lt=in[x]; e[cnt].f=f; in[x]=cnt; e[++cnt].to=x; e[cnt].lt=in[y]; e[cnt].f=0; in[y]=cnt; } bool bfs() { while(!que.empty()) que.pop(); memset(dep,0,sizeof(dep)); que.push(s); dep[s]=1; while(!que.empty()) { int x=que.front(); que.pop(); for(int i=in[x];i;i=e[i].lt) if(e[i].f&&!dep[e[i].to]) { que.push(e[i].to),dep[e[i].to]=dep[x]+1; if(e[i].to==t) return 1; } } return 0; } int dfs(int x,int flow) { if(x==t) return flow; int cur=flow; for(int i=in[x];i;i=e[i].lt) if(e[i].f&&dep[e[i].to]==dep[x]+1) { int tmp=dfs(e[i].to,min(e[i].f,cur)); e[i].f-=tmp; e[i^1].f+=tmp; cur-=tmp; if(!cur) return flow; } dep[x]=-1; return flow-cur; } int dinic() { int ans=0; while(bfs()) ans+=dfs(s,inf); return ans; } int a[41][41][41],p,q,r,d,dx[4]={0,0,1,-1},dy[4]={1,-1,0,0}; int id(int x,int y,int d) { if(!d) return s; return (d-1)*p*q+(x-1)*q+y; } int main() { s=N-3; t=s+1; p=read(),q=read(),r=read(),d=read(); for(int i=1;i<=r;i++) for(int j=1;j<=p;j++) for(int k=1;k<=q;k++) a[j][k][i]=read(); for(int i=1;i<=p;i++) for(int j=1;j<=q;j++) { for(int k=1;k<=r;k++) add(id(i,j,k-1),id(i,j,k),a[i][j][k]); add(id(i,j,r),t,inf); } for(int i=1;i<=p;i++) for(int j=1;j<=q;j++) for(int w=0;w<4;w++) { int x=dx[w]+i,y=dy[w]+j; if(!x||!y||x>p||y>q) continue; for(int k=d+1;k<=r;k++) add(id(i,j,k),id(x,y,k-d),inf); } printf("%d\n",dinic()); return 0; }
bzoj2229 ZJOI2011 最小割
最小割树!板子题不说了qwq //Love and Freedom. #include<cstdio> #include<algorithm> #include<cstring> #include<cmath> #include<vector> #include<queue> #define ll long long #define inf 2002122500 #define M 12010 #define N 401 #define pa pair<int,int> #define mp make_pair #define fs first #define se second #define pb push_back using namespace std; int read() { int s=0,f=1; char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-') f=-1; ch=getchar();} while(ch>='0'&&ch<='9') s=s*10+ch-'0',ch=getchar(); return f*s; } struct edge{int to,lt,f;}e[M]; int ful[M]; int in[N],cnt; vector<pa> tr[N]; void init(int n) { memset(in,0,sizeof(in)); cnt=1; for(int i=1;i<=n;i++) tr[i].clear(); } void add(int x,int y,int f) { e[++cnt].to=y; e[cnt].lt=in[x]; e[cnt].f=f; in[x]=cnt; ful[cnt]=f; e[++cnt].to=x; e[cnt].lt=in[y]; e[cnt].f=f; in[y]=cnt; ful[cnt]=f; } void link(int x,int y,int f) { tr[x].pb(mp(y,f)); tr[y].pb(mp(x,f)); } queue<int> q; int dep[N]; bool bfs(int s,int t) { while(!q.empty()) q.pop(); memset(dep,0,sizeof(dep)); q.push(s); dep[s]=1; while(!q.empty()) { int x=q.front(); q.pop(); for(int i=in[x];i;i=e[i].lt) if(!dep[e[i].to]&&e[i].f) { q.push(e[i].to); dep[e[i].to]=dep[x]+1; if(e[i].to==t) return 1; } } return 0; } int dfs(int t,int x,int flow) { if(x==t) return flow; int cur=flow; for(int i=in[x];i;i=e[i].lt) if(e[i].f&&dep[e[i].to]==dep[x]+1) { int tmp=dfs(t,e[i].to,min(cur,e[i].f)); e[i].f-=tmp; e[i^1].f+=tmp; cur-=tmp; if(!cur) return flow; } dep[x]=-1; return flow-cur; } void dinic(int s,int t) { int ans=0; for(int i=2;i<=cnt;i++) e[i].f=ful[i]; while(bfs(s,t)) ans+=dfs(t,s,inf); link(s,t,ans);// printf("%d %d %d\n",s,t,ans); } int id[N],ss[N],tt[N]; void build(int *a,int n) { if(n<2) return; dinic(a[0],a[1]); int tops=0,topt=0; for(int i=0;i<n;i++) if(dep[a[i]]) ss[tops++]=a[i]; else tt[topt++]=a[i]; memcpy(a,ss,tops<<2); memcpy(a+tops,tt,topt<<2); build(a,tops); build(a+tops,topt); } vector<int> ans; void dfs2(int x,int f,int v) { for(int i=0;i<tr[x].size();i++) { pa tmp=tr[x][i]; int y=tmp.fs,val=min(v,tmp.se); if(y==f) continue; ans.pb(val); dfs2(y,x,val); } } void calc(int n) { ans.clear(); for(int i=1;i<=n;i++) dfs2(i,i,inf); } int main() { int T=read(),x,y,v; while(T--) { cnt=1; int n=read(),m=read(); for(int i=1;i<=m;i++) x=read(),y=read(),v=read(),add(x,y,v); for(int i=0;i<n;i++) id[i]=i+1; build(id,n); calc(n); sort(ans.begin(),ans.end()); int Q=read(); while(Q--) { x=read(); printf("%d\n",(upper_bound(ans.begin(),ans.end(),x)-ans.begin())>>1); } init(n); puts(""); } return 0; }
bzoj4519 CQOI2016 不同的最小割
板子++ //Love and Freedom. #include<cstdio> #include<algorithm> #include<cstring> #include<cmath> #include<vector> #include<queue> #define ll long long #define inf 2002122500 #define M 20010 #define N 1010 #define pa pair<int,int> #define mp make_pair #define fs first #define se second #define pb push_back using namespace std; int read() { int s=0,f=1; char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-') f=-1; ch=getchar();} while(ch>='0'&&ch<='9') s=s*10+ch-'0',ch=getchar(); return f*s; } struct edge{int to,lt,f;}e[M]; int ful[M]; int in[N],cnt; vector<int> len; void add(int x,int y,int f) { e[++cnt].to=y; e[cnt].lt=in[x]; e[cnt].f=f; in[x]=cnt; ful[cnt]=f; e[++cnt].to=x; e[cnt].lt=in[y]; e[cnt].f=f; in[y]=cnt; ful[cnt]=f; } queue<int> q; int dep[N]; bool bfs(int s,int t) { while(!q.empty()) q.pop(); memset(dep,0,sizeof(dep)); q.push(s); dep[s]=1; while(!q.empty()) { int x=q.front(); q.pop(); for(int i=in[x];i;i=e[i].lt) if(!dep[e[i].to]&&e[i].f) { q.push(e[i].to); dep[e[i].to]=dep[x]+1; if(e[i].to==t) return 1; } } return 0; } int dfs(int t,int x,int flow) { if(x==t) return flow; int cur=flow; for(int i=in[x];i;i=e[i].lt) if(e[i].f&&dep[e[i].to]==dep[x]+1) { int tmp=dfs(t,e[i].to,min(cur,e[i].f)); e[i].f-=tmp; e[i^1].f+=tmp; cur-=tmp; if(!cur) return flow; } dep[x]=-1; return flow-cur; } void dinic(int s,int t) { int ans=0; for(int i=2;i<=cnt;i++) e[i].f=ful[i]; while(bfs(s,t)) ans+=dfs(t,s,inf); len.pb(ans); } int id[N],ss[N],tt[N]; void build(int *a,int n) { if(n<2) return; dinic(a[0],a[1]); int tops=0,topt=0; for(int i=0;i<n;i++) if(dep[a[i]]) ss[tops++]=a[i]; else tt[topt++]=a[i]; memcpy(a,ss,tops<<2); memcpy(a+tops,tt,topt<<2); build(a,tops); build(a+tops,topt); } int main() { int x,y,v; cnt=1; int n=read(),m=read(); for(int i=1;i<=m;i++) x=read(),y=read(),v=read(),add(x,y,v); for(int i=0;i<n;i++) id[i]=i+1; build(id,n); sort(len.begin(),len.end()); printf("%d\n",unique(len.begin(),len.end())-len.begin()); return 0; }
发现自己学的可能是假的最大权闭合子图??? 貌似合理的应该是当权>=0 是(s,i,a[i]) <0是(i,t,-a[i])然后需要割需要割掉的-正的所有。。。 发现这个东西就是满足Hall定理,保证有匹配。其实就是集合对应的所有数必须要覆盖掉。。。所以就是最大权闭合子图模型了。。。 //Love and Freedom. #include<cstdio> #include<algorithm> #include<cstring> #include<cmath> #include<queue> #define ll long long #define inf 2002122500 #define N 310 using namespace std; int read() { int s=0,f=1; char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-') f=-1; ch=getchar();} while(ch>='0'&&ch<='9') s=s*10+ch-'0',ch=getchar(); return f*s; } struct edge{int to,lt,f;}e[N*N*4]; queue<int> que; int dep[N],cnt=1,in[N],s,t; void add(int x,int y,int f) { e[++cnt].to=y; e[cnt].lt=in[x]; e[cnt].f=f; in[x]=cnt; e[++cnt].to=x; e[cnt].lt=in[y]; e[cnt].f=0; in[y]=cnt; } bool bfs() { while(!que.empty()) que.pop(); memset(dep,0,sizeof(dep)); que.push(s); dep[s]=1; while(!que.empty()) { int x=que.front(); que.pop(); for(int i=in[x];i;i=e[i].lt) if(e[i].f&&!dep[e[i].to]) { que.push(e[i].to),dep[e[i].to]=dep[x]+1; if(e[i].to==t) return 1; } } return 0; } int dfs(int x,int flow) { if(x==t) return flow; int cur=flow; for(int i=in[x];i;i=e[i].lt) if(e[i].f&&dep[e[i].to]==dep[x]+1) { int tmp=dfs(e[i].to,min(e[i].f,cur)); e[i].f-=tmp; e[i^1].f+=tmp; cur-=tmp; if(!cur) return flow; } dep[x]=-1; return flow-cur; } int dinic() { int ans=0; while(bfs()) ans+=dfs(s,inf); return ans; } int k[N],edg[N][N],p[N],a[N],n; bool vis[N]; bool match(int x) { for(int i=1;i<=k[x];i++) if(!vis[edg[x][i]]) { vis[edg[x][i]]=1; if(!p[edg[x][i]] || match(p[edg[x][i]])) return p[edg[x][i]]=x,true; } return false; } void Match() { for(int i=1;i<=n;i++) { memset(vis,0,sizeof(vis)); match(i); } } void build() { for(int i=1;i<=n;i++) for(int j=1;j<=k[i];j++) if(p[edg[i][j]]!=i) add(i,p[edg[i][j]],inf); } int main() { s=N-3; t=s+1; n=read(); for(int i=1;i<=n;i++) { k[i]=read(); for(int j=1;j<=k[i];j++) edg[i][j]=read(); } int ful=0; for(int i=1;i<=n;i++) { a[i]=-read(); if(a[i]>=0) add(s,i,a[i]),ful+=a[i]; else add(i,t,-a[i]); } Match(); build(); printf("%d\n",dinic()-ful); return 0; }
bzoj4842 Delight For A Cat
距离限制建模...今天有点咕,明天写完另一道一起总结吧...117组数据好毒啊.. //Love and Freedom. #include<cstdio> #include<algorithm> #include<cstring> #include<cmath> #include<queue> #define ll long long #define inf 20021225 #define N 2010 using namespace std; int read() { int s=0,f=1; char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-') f=-1; ch=getchar();} while(ch>='0'&&ch<='9') s=s*10+ch-'0',ch=getchar(); return f*s; } struct edge{int to,lt,f,fr;ll c;}e[N*20]; int in[N],cnt=1,from[N],a[N],b[N]; bool vis[N]; void add(int x,int y,int f,ll c) { e[++cnt].to=y; e[cnt].lt=in[x]; in[x]=cnt; e[cnt].fr=x; e[cnt].f=f; e[cnt].c=c; e[++cnt].to=x; e[cnt].lt=in[y]; in[y]=cnt; e[cnt].fr=y; e[cnt].f=0; e[cnt].c=-c; } queue<int> q; ll dis[N]; int s,t; bool spfa() { memset(dis,63,sizeof(dis)); dis[s]=0; q.push(s); vis[s]=1; while(!q.empty()) { int x=q.front(); q.pop(); vis[x]=0; for(int i=in[x];i;i=e[i].lt) { int y=e[i].to; if(e[i].f&&dis[y]>dis[x]+e[i].c) { dis[y]=dis[x]+e[i].c; from[y]=i; if(!vis[y]) q.push(y),vis[y]=1; } } } return dis[t]<dis[0]; } ll dinic() { ll ans=0; while(spfa()) { int flow=inf; for(int i=from[t];i;i=from[e[i].fr]) flow=min(flow,e[i].f); for(int i=from[t];i;i=from[e[i].fr]) ans+=flow*e[i].c,e[i].f-=flow,e[i^1].f+=flow; //printf("%d %lld\n",flow,dis[t]); } return ans; } int id[N]; int main() { int n=read(),k=read(),t1=read(),t2=read(); ll ans=0; s=N-3; t=s+1; int ss=t+1; add(s,ss,k-t1,0); for(int i=1;i<=k;i++) add(ss,i,inf,0); for(int i=1;i<=n;i++) { a[i]=read(),ans+=a[i]; add(i,i+1<=n?i+1:t,k-t1-t2,0); } for(int i=1;i<=n;i++) { b[i]=read(); add(i,i+k<=n?i+k:t,1,a[i]-b[i]); id[i]=cnt; } printf("%lld\n",ans-dinic()); for(int i=1;i<=n;i++) if(e[id[i]].f) printf("E"); else printf("S"); printf("\n"); return 0; }
bzoj2324 营救皮卡丘
一种做法是建分层图然后跑上下界费用流 显然不是很好做 另一种就是用k条路径覆盖所有点 其中两点距离需要特殊处理(只能走小的点) 这个的话类似于路径覆盖,不要刻意按照建图理解,按照“找出路”的思路理解比较方便。qwq。 //Love and Freedom. #include<cstdio> #include<algorithm> #include<cstring> #include<cmath> #include<queue> #define ll long long #define inf 20021225 #define N 420 #define M 100001 using namespace std; int read() { int s=0,f=1; char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-') f=-1; ch=getchar();} while(ch>='0'&&ch<='9') s=s*10+ch-'0',ch=getchar(); return f*s; } int d[N][N]; struct edge{int fr,to,lt,f,c;}e[M<<1]; int cnt=1,in[N],from[N],dis[N],s,t; queue<int> q; bool vis[N]; void add(int x,int y,int f,int c) { e[++cnt].to=y; e[cnt].lt=in[x]; in[x]=cnt; e[cnt].f=f; e[cnt].c=c; e[cnt].fr=x; e[++cnt].to=x; e[cnt].lt=in[y]; in[y]=cnt; e[cnt].f=0; e[cnt].c=-c; e[cnt].fr=y; } bool spfa() { memset(dis,48,sizeof(dis)); q.push(s); dis[s]=0; vis[s]=1; while(!q.empty()) { int x=q.front(); q.pop(); vis[x]=0; for(int i=in[x];i;i=e[i].lt) { int y=e[i].to; if(e[i].f&&dis[y]>dis[x]+e[i].c) { dis[y]=dis[x]+e[i].c; from[y]=i; if(!vis[y]) vis[y]=1,q.push(y); } } } return dis[t]<dis[N-1]; } int dinic() { int ans=0; while(spfa()) { int flow=inf; for(int i=from[t];i;i=from[e[i].fr]) flow=min(flow,e[i].f); for(int i=from[t];i;i=from[e[i].fr]) e[i].f-=flow,e[i^1].f+=flow,ans+=flow*e[i].c; } return ans; } int main() { int n=read(),m=read(),k=read(); s=N-3; t=s+1; memset(d,48,sizeof(d)); for(int i=1;i<=m;i++) { int a=read(),b=read(),l=read(); d[a][b]=d[b][a]=min(d[a][b],l); } for(int i=0;i<=n;i++) d[i][i]=0; for(int k=0;k<=n;k++) for(int i=0;i<=n;i++) for(int j=0;j<=n;j++) if(k<=i||k<=j) d[i][j]=min(d[i][j],d[i][k]+d[k][j]); add(s,0,k,0); for(int i=1;i<=n;i++) add(s,i,1,0),add(i+n,t,1,0); for(int i=0;i<=n;i++) for(int j=i+1;j<=n;j++) if(d[i][j]<d[N-1][N-1]) add(i,j+n,1,d[i][j]); printf("%d\n",dinic()); return 0; }
bzoj2597 WC2007 石头剪刀布
计数题是怎么能想到网络流的啊喂。 正难则反,我们考虑三元组不构成三元环的方式,只有一种就是一个点两个入度,一个点两个出度,一个一入一出。我们考虑统计出度的。 构图比较显然,s向每个竞赛点连(1,0),竞赛点再向i,j分别连(1,0)。每个人再向t连边,但我们发现这个贡献是一个二次的柿子C(wi,2)=(wi-1)*wi/2,发现长的和高斯式一样,凸费用拆边就可以啦。 最后要注意已经有了的需要单独统计。 //Love and Freedom. #include<cstdio> #include<algorithm> #include<cstring> #include<cmath> #include<queue> #define ll long long #define inf 20021225 #define N 220 #define M 500001 using namespace std; int read() { int s=0,f=1; char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-') f=-1; ch=getchar();} while(ch>='0'&&ch<='9') s=s*10+ch-'0',ch=getchar(); return f*s; } int d[N][N],tot; struct edge{int fr,to,lt,f,c;}e[M<<1]; int cnt=1,in[N*N],from[N*N],dis[N*N],s,t; queue<int> q; bool vis[N*N]; void add(int x,int y,int f,int c) { e[++cnt].to=y; e[cnt].lt=in[x]; in[x]=cnt; e[cnt].f=f; e[cnt].c=c; e[cnt].fr=x; e[++cnt].to=x; e[cnt].lt=in[y]; in[y]=cnt; e[cnt].f=0; e[cnt].c=-c; e[cnt].fr=y; } bool spfa() { memset(dis,48,sizeof(dis)); q.push(s); dis[s]=0; vis[s]=1; while(!q.empty()) { int x=q.front(); q.pop(); vis[x]=0; for(int i=in[x];i;i=e[i].lt) { int y=e[i].to; if(e[i].f&&dis[y]>dis[x]+e[i].c) { dis[y]=dis[x]+e[i].c; from[y]=i; if(!vis[y]) vis[y]=1,q.push(y); } } } return dis[t]<dis[0]; } int dinic() { int ans=0; while(spfa()) { int flow=inf; for(int i=from[t];i;i=from[e[i].fr]) flow=min(flow,e[i].f); for(int i=from[t];i;i=from[e[i].fr]) e[i].f-=flow,e[i^1].f+=flow,ans+=flow*e[i].c; } return ans; } int w[N],id[N][N]; int main() { //freopen("in.txt","r",stdin); //freopen("ans.txt","w",stdout); int n=read(); s=N*N-3; t=s+1; tot=n; int ans=0; for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) { id[i][j]=read(); w[i]+=id[i][j]==1; if(id[i][j]==2 && i<j) id[i][j]=++tot,add(tot,i,1,0),d[i][j]=cnt,add(tot,j,1,0),d[j][i]=cnt; } for(int i=n+1;i<=tot;i++) add(s,i,1,0); for(int i=1;i<=n;i++) { ans+=w[i]*(w[i]-1)/2; for(int j=w[i];j<=n;j++) add(i,t,1,j); } printf("%d\n",n*(n-1)*(n-2)/6-ans-dinic()); for(int i=1;i<=n;i++,printf("\n")) for(int j=1;j<=n;j++) { if(d[i][j]) printf("%d ",e[d[i][j]].f?1:0); else printf("%d ",id[i][j]); } return 0; }
CF910E Fox And Dinner
发现每个数>=2所以是二分图,环长度>=3就每个点找入点出点就可以了。 //Love and Freedom. #include<cstdio> #include<algorithm> #include<cstring> #include<cmath> #include<vector> #include<queue> #define ll long long #define inf 2002122500 #define M 100010 #define N 410 #define pb push_back using namespace std; int read() { int s=0,f=1; char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-') f=-1; ch=getchar();} while(ch>='0'&&ch<='9') s=s*10+ch-'0',ch=getchar(); return f*s; } struct edge{int to,lt,f;}e[M]; int in[N],cnt=1,s,t,a[N]; void add(int x,int y,int f) { e[++cnt].to=y; e[cnt].lt=in[x]; e[cnt].f=f; in[x]=cnt; e[++cnt].to=x; e[cnt].lt=in[y]; e[cnt].f=0; in[y]=cnt; } queue<int> q; int dep[N]; bool bfs() { while(!q.empty()) q.pop(); memset(dep,0,sizeof(dep)); q.push(s); dep[s]=1; while(!q.empty()) { int x=q.front(); q.pop(); for(int i=in[x];i;i=e[i].lt) if(!dep[e[i].to]&&e[i].f) { q.push(e[i].to); dep[e[i].to]=dep[x]+1; if(e[i].to==t) return 1; } } return 0; } int dfs(int x,int flow) { if(x==t) return flow; int cur=flow; for(int i=in[x];i;i=e[i].lt) if(e[i].f&&dep[e[i].to]==dep[x]+1) { int tmp=dfs(e[i].to,min(cur,e[i].f)); e[i].f-=tmp; e[i^1].f+=tmp; cur-=tmp; if(!cur) return flow; } dep[x]=-1; return flow-cur; } int dinic() { int ans=0; while(bfs()) ans+=dfs(s,inf); //printf("%d\n",ans); return ans; } bool isp(int x) { for(int i=2;i*i<=x;i++) if(x%i==0) return 0; return 1; } vector<int> ans[N]; bool vis[N]; void go(int k,int x) { if(vis[x]) return; vis[x]=1; ans[k].pb(x); //printf("%d ",x); for(int i=in[x];i;i=e[i].lt) { if(e[i].to==s || e[i].to==t) continue; //printf("*"); //printf("%d %d %d\n",x,e[i].to,e[i].f); if((a[x]&1) && !e[i].f) go(k,e[i].to); if((!(a[x]&1)) && e[i].f) go(k,e[i].to); } } int main() { int n=read(); s=N-3; t=s+1; for(int i=1;i<=n;i++) a[i]=read(); for(int i=1;i<=n;i++) { if(a[i]&1) add(s,i,2); else add(i,t,2); } for(int i=1;i<=n;i++) if(a[i]&1) { for(int j=1;j<=n;j++) if(isp(a[i]+a[j])) add(i,j,1); } if(dinic()<n) printf("Impossible\n"); else { int fin=0; for(int i=1;i<=n;i++) if(!vis[i]) go(++fin,i); printf("%d\n",fin); for(int i=1;i<=fin;i++) { printf("%d ",ans[i].size()); for(int j=0;j<ans[i].size();j++) printf("%d ",ans[i][j]); printf("\n"); } } return 0; }
bzoj2879 美食节
和SCOI2007修车是一道题,只不过数据范围扩大了。 考虑暴力建图的话我们会有约6e6条边,显然没戏。 但是我们只需要跑sigma(pi)次最小费用流,所以我们考虑动态开点。 即每次找到增广路上的那个厨师,让它继续扩展一道菜,这样的话,我们的复杂度就是对的了。O(np^2lgN)。(SC选手nb啊 //Love and Freedom. #include<cstdio> #include<algorithm> #include<cstring> #include<cmath> #include<queue> #define ll long long #define inf 20021225 #define N 4200 #define M 500001 using namespace std; int read() { int s=0,f=1; char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-') f=-1; ch=getchar();} while(ch>='0'&&ch<='9') s=s*10+ch-'0',ch=getchar(); return f*s; } int d[N][N]; struct edge{int fr,to,lt,f,c;}e[M<<1]; int cnt=1,in[N],from[N],dis[N],s,t; queue<int> q; bool vis[N]; void add(int x,int y,int f,int c) { e[++cnt].to=y; e[cnt].lt=in[x]; in[x]=cnt; e[cnt].f=f; e[cnt].c=c; e[cnt].fr=x; e[++cnt].to=x; e[cnt].lt=in[y]; in[y]=cnt; e[cnt].f=0; e[cnt].c=-c; e[cnt].fr=y; } bool spfa() { memset(dis,48,sizeof(dis)); q.push(s); dis[s]=0; vis[s]=1; while(!q.empty()) { int x=q.front(); q.pop(); vis[x]=0; for(int i=in[x];i;i=e[i].lt) { int y=e[i].to; if(e[i].f&&dis[y]>dis[x]+e[i].c) { dis[y]=dis[x]+e[i].c; from[y]=i; if(!vis[y]) vis[y]=1,q.push(y); } } } return dis[t]<dis[N-1]; } int dinic() { int ans=0; int flow=inf; for(int i=from[t];i;i=from[e[i].fr]) flow=min(flow,e[i].f); for(int i=from[t];i;i=from[e[i].fr]) e[i].f-=flow,e[i^1].f+=flow,ans+=flow*e[i].c; return ans; } int p[N],poi,w[N][N],ans,id[N]; void upd() { ans+=dinic(); int x=e[from[t]].fr; id[++poi]=id[x]; add(poi,t,1,0); for(int i=in[x];i;i=e[i].lt) { int y=e[i].to,v=e[i^1].c; if(y==t) continue; v+=w[y][id[x]]; add(y,poi,1,v); } } int main() { int n=read(),m=read(); s=N-3; t=s+1; for(int i=1;i<=n;i++) p[i]=read(),add(s,i,p[i],0); poi=n+m; for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) w[i][j]=read(), add(i,n+j,1,w[i][j]); for(int i=1;i<=m;i++) add(n+i,t,1,0),id[n+i]=i; while(spfa()) upd(); printf("%d\n",ans); return 0; }
bzoj3218 a+b problem
终于把它写了... 考虑建图,S->i b[i] i->T w[i] i->i' p[i] i'->j inf 每次新建一条链来保证重复的不会冲突。 就是基本的主席树优化建图吧... //Love and Freedom. #include<cstdio> #include<algorithm> #include<cstring> #include<cmath> #include<queue> #define ll long long #define inf 20021225 #define N 500010 #define ls(x) T[x].son[0] #define rs(x) T[x].son[1] using namespace std; int read() { int s=0,f=1; char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-') f=-1; ch=getchar();} while(ch>='0'&&ch<='9') s=s*10+ch-'0',ch=getchar(); return f*s; } struct edge{int to,lt,f;}e[N<<1]; struct node{int son[2],l,r;}T[N]; int in[N],cnt=1,poi,dep[N],s,t; queue<int> q; void add(int x,int y,int f) { e[++cnt].to=y; e[cnt].lt=in[x]; in[x]=cnt; e[cnt].f=f; e[++cnt].to=x; e[cnt].lt=in[y]; in[y]=cnt; e[cnt].f=0; } bool bfs() { while(!q.empty()) q.pop(); memset(dep,0,sizeof(dep)); q.push(s); dep[s]=1; while(!q.empty()) { int x=q.front(); q.pop(); for(int i=in[x];i;i=e[i].lt) { int y=e[i].to; if(!dep[y]&&e[i].f) { dep[y]=dep[x]+1,q.push(y); if(y==t) return 1; } } } return 0; } int dfs(int x,int flow) { if(x==t) return flow; int cur=flow; for(int i=in[x];i;i=e[i].lt) if(dep[e[i].to]==dep[x]+1&&e[i].f) { int tmp=dfs(e[i].to,min(e[i].f,cur)); e[i].f-=tmp; e[i^1].f+=tmp; cur-=tmp; if(!cur) return flow; } dep[x]=-1; return flow-cur; } int dinic() { int ans=0; while(bfs()) ans+=dfs(s,inf); return ans; } void query(int x,int l,int r,int LL,int RR,int fr) { if(LL<=l&&RR>=r){add(fr,x,inf); return;} int mid=l+r>>1; if(LL<=mid) query(ls(x),l,mid,LL,RR,fr); if(RR>mid) query(rs(x),mid+1,r,LL,RR,fr); } void modify(int &x,int l,int r,int p,int to) { T[++poi]=T[x]; add(poi,x,inf); x=poi; add(x,to,inf); if(l==r) return; int mid=l+r>>1; if(p<=mid) modify(ls(x),l,mid,p,to); else modify(rs(x),mid+1,r,p,to); } int val[N],tot,n,a[N],b[N],l[N],r[N],w[N],p[N]; int main() { n=read(); s=N-2; t=s+1; poi=n*2; int ans=0; for(int i=1;i<=n;i++) { a[i]=read(),b[i]=read(),w[i]=read(),l[i]=read(),r[i]=read(),p[i]=read(); add(i,n+i,p[i]); add(s,i,b[i]); add(i,t,w[i]); val[++tot]=a[i]; val[++tot]=l[i]; val[++tot]=r[i]; ans+=b[i]+w[i]; } sort(val+1,val+tot+1); tot=unique(val+1,val+tot+1)-val-1; int rt=0; for(int i=1;i<=n;i++) { a[i]=lower_bound(val+1,val+tot+1,a[i])-val; l[i]=lower_bound(val+1,val+tot+1,l[i])-val; r[i]=lower_bound(val+1,val+tot+1,r[i])-val; query(rt,1,tot,l[i],r[i],i+n); modify(rt,1,tot,a[i],i); } printf("%d\n",ans-dinic()); return 0; }
做网络流也发现自己没有脑子...