网络流套路小结

讲课的时候发现自己好像网络流好像还行。。。

但是建模套路还是很多 我这种脑子是不可能记住的 所以写一个总结吧

需要掌握/实现/没写过的标出来了

最小割建模:

最大权闭合子图 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;
}
View Code

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;
}
View Code

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;
}
View Code

 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;
}
View Code
CF103E Buying Sets
发现自己学的可能是假的最大权闭合子图???
貌似合理的应该是当权>=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;
}
View Code

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;
}
View Code

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;
}
View Code

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;
}
View Code

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;
}
View Code

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;
}
View Code

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;
}
View Code

做网络流也发现自己没有脑子...

posted @ 2019-08-12 12:37  寒雨微凝  阅读(397)  评论(0编辑  收藏  举报