首页 > 代码库 > Codeforces 786E. ALT 最小割+倍增

Codeforces 786E. ALT 最小割+倍增

E. ALT

http://codeforces.com/problemset/problem/786/E

题意:

给出一棵 n 个节点的树与 m 个工人。
每个工人有一条上下班路线(简单路径),一个工人会得到满足只要下面一项满足:
1、他得到一只puppy
2、他的上下班路径上每条边都有一只doge
求使所有工人满足的最小dog数并输出方案。n≤20000,m≤10000

 

想法:暴力建图,源点连向工人Xi,边Yi连向汇点,容量为1。然后工人向其路径上的边连容量为1的边。O(nm)

优化建图:路径为树上连续路径,那么可以套上树链剖分变成连续区间,再用线段树优化连边....O(mlog^2n)。

反正不带修改,那么求树上路径问题还可以用点分治和倍增。O(mlogn)

点分治:对于同一个重心,维护一条轨道,通过轨道可以到达这个点到重心所有点。

具体:add(D[i],Y[i]),add(D[i],D[i-1]);

倍增:可以形象成树上一条链被拆分成若干个区间,步长为2^j,大区间向其包含的小区间连边,这个类似线段树优化建边。具体:add(G[j][x],G[j-1][x]),add(G[j][x],G[j-1][ F[j-1][x] ]);

最小割方案的输出:对于Xi如果划分到{T}集合,那么他与源点的边就被割了,他得到了一只puppy。如果Yi被划分到{S},那么它与汇点的边被割了,一只doge放在这条边上。

附上最小割+倍增的代码(点分治好麻烦....懒癌晚期患者表示不想写)

技术分享
#include<cstdio>
#include<cmath>
#include<vector>
const int len(20000),N(400000),INF(0x7fffffff);
struct Node{int nd,nx,pos;}bot[len*2+10];int tot,first[len+10],v,u;
int f[16][len+10],g[16][len+10],logg,depth[len+10],pos[len+10];
struct Data{int nd,fl,nx;};
std::vector<Data>Edge[N+10];int S,T;
int n,m,ans,now,last,st[len+10],top,flag[N+10];;
int min(int a,int b){return a>b?b:a;}
void swap(int &a,int &b){if(a==b)return;a^=b;b^=a;a^=b;}
void add(int a,int b,int p){bot[++tot]=(Node){b,first[a],p};first[a]=tot;}
void addedge(int a,int b,int f)
{
    Edge[a].push_back((Data){b,f,(int)Edge[b].size()});
    Edge[b].push_back((Data){a,0,(int)Edge[a].size()-1});
}
template <class T>void read(T &x)
{
    x=0;char ch=getchar();
    while(ch<0||ch>9)ch=getchar();
    while(ch>=0&&ch<=9){x=x*10+ch-0;ch=getchar();}
}
void update(int x) 
{
    for(int j=1;j<=logg;j++)
    {
        f[j][x]=f[j-1][ f[j-1][x] ];
        g[j][x]=++now;
        addedge(g[j][x],g[j-1][x],INF);
        addedge(g[j][x],g[j-1][ f[j-1][x] ],INF);
    }
}
void Dfs(int x,int fa)
{
    f[0][x]=fa; g[0][x]=x; 
    update(x); depth[x]=depth[fa]+1;
    for(int v=first[x];v;v=bot[v].nx)
    if(bot[v].nd!=fa) pos[bot[v].nd]=bot[v].pos,Dfs(bot[v].nd,x);
}
int lca(int u,int v)
{
    if(depth[u]<depth[v])swap(u,v);
    for(int k=depth[u]-depth[v],j=0;k;k>>=1,j++) if(k&1)u=f[j][u];
    if(u==v)return u;
    for(int j=logg;j>=0&&f[0][v]!=f[0][u];j--)
    if(f[j][u]!=f[j][v])u=f[j][u],v=f[j][v];
    return f[0][v];
}
void link(int x,int y,int t)
{
    for(int k=depth[x]-depth[t],j=0;k;k>>=1,j++)//LCA不选 
    if(k&1)addedge(now,g[j][x],INF),x=f[j][x];
    for(int k=depth[y]-depth[t],j=0;k;k>>=1,j++)//LCA不选 
    if(k&1)addedge(now,g[j][y],INF),y=f[j][y];
}
void DFS_Put(int x)
{
    flag[x]=1;
    for(int v=0,sz=Edge[x].size();v<sz;v++)
    {
        Data y=Edge[x][v];
        if(y.fl&&!flag[y.nd])DFS_Put(y.nd);
    }
}
int q[N+10],l,h,dis[N+10];
bool bfs(int s,int t)
{
    for(int i=1;i<=T;i++)dis[i]=INF;
    q[l=1]=s; h=0; dis[s]=0;
    while(h<l)
    {
        int x=q[++h];
        for(int v=0,sz=Edge[x].size();v<sz;v++)
        {
            Data y=Edge[x][v];
            if(y.fl&&dis[y.nd]==INF)
            dis[y.nd]=dis[x]+1,q[++l]=y.nd;
        }
    }
    return dis[t]!=INF;
}
int dfs(int x,int t,int flow)
{
    if(x==t)return flow;
    int sum=0,tmp;
    for(int v=0,sz=Edge[x].size();v<sz;v++)
    {
        Data y=Edge[x][v];
        if(y.fl&&dis[y.nd]==dis[x]+1)
        {
            tmp=dfs(y.nd,t,min(y.fl,flow));
            flow-=tmp; sum+=tmp;
            Edge[x][v].fl-=tmp;
            Edge[y.nd][y.nx].fl+=tmp;
            if(!flow)break;
        }
    }
    if(!sum)dis[x]=-1;
    return sum;
}

int main()
{
    read(n),read(m);logg=log2(n);
    for(int i=1;i<n;i++)
    {
        read(u),read(v);
        add(u,v,i),add(v,u,i);
    }
    now=n;
    Dfs(1,0);
    S=now+m+1; T=S+1; last=now;
    for(int i=1;i<=n;i++)addedge(i,T,1);
    for(int i=1,Lt;i<=m;i++)
    {
        read(v),read(u);
        now++; addedge(S,now,1);
        Lt=lca(v,u);
        link(v,u,Lt); 
    }
    while(bfs(S,T))ans+=dfs(S,T,INF);
    printf("%d\n",ans);
    DFS_Put(S);
    for(int i=1;i<=m;i++) if(!flag[i+last])st[++top]=i;
    printf("%d ",top);
    while(top)printf("%d ",st[top--]);
    for(int i=1;i<=n;i++) if(flag[i])st[++top]=pos[i];
    printf("\n");
    printf("%d ",top);
    while(top)printf("%d ",st[top--]);    
    return 0;
}
View Code

 

Codeforces 786E. ALT 最小割+倍增