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

时间: 2024-10-13 06:33:17

Codeforces 786E. ALT 最小割+倍增的相关文章

cf786E ALT (最小割+倍增优化建图)

如果把“我全都要”看作是我全不要的话,就可以用最小割解决啦 源点S,汇点T 我们试图让每个市民作为一个等待被割断的路径 把狗狗给市民:建边(S,i,1),其中i是市民 把狗狗给守卫:建边(j,T,1),其中j是守卫(也就是边) 市民要在路上所有边看到狗:建边(i,j,1),其中i是市民,j是i经过的边 (众所周知,(!A)&(!B)==!(A|B),所以要把两种选择串起来) 然而这样边数太多了,考虑倍增来优化建边 ...反正就是倍增优化建边,流量给正无穷 最大流=最小割,跑个dinic就行了(要

Educational Codeforces Round 21 Problem F (Codeforces 808F) - 最小割 - 二分答案

Digital collectible card games have become very popular recently. So Vova decided to try one of these. Vova has n cards in his collection. Each of these cards is characterised by its power pi, magic number ci and level li. Vova wants to build a deck

Codeforces 724E Goods transportation(最小割转DP)

[题目链接] http://codeforces.com/problemset/problem/724/E [题目大意] 每个城市有pi的物品可以运出去卖,si个物品可以买, 编号小的城市可以往编号大的城市最多运送c的物品,问最多有多少物品可以被买卖 [题解] 源点向每个城市引pi的流,每个城市向汇点引si的流, 小编号的城市往大编号的城市引c的流,那么全图的最大流就是答案, 但是数据量过大,我们考虑转化. 因为最大流等于最小割,我们发现对于这个图,最后每个点不是跟s连就是跟t连, 那么我们设d

[2016北京集训试题6]网络战争-[最小割树(网络流)+kd-tree+倍增]

Description A 联邦国有 N 个州,每个州内部都有一个网络系统,有若干条网络线路,连接各个 州内部的城市. 由于 A 国的州与州之间的关系不是太好,每个州都只有首府建立了到别的州的网络.具体来说,每个州的首府都只主动地建立了一条网络线路,连接到距离最近的州的 首府.(欧氏距离.如果有多个,选择标号最小的去连接) B 国探知了 A 国的网络线路分布情况,以及攻陷每条网络线路所需花费的代价,B 国首脑想知道断开 A 国某两个城市之间的网络连接,所需的最少代价.请你计算出来告 诉他. 注:

Codeforces 1009G Allowed Letters 最大流转最小割 sosdp

Allowed Letters 最直观的想法是贪心取, 然后网络流取check可不可行, 然后T了. 想到最大流可以等于最小割, 那么我们状压枚举字符代表的6个点连向汇点是否断掉, 然后再枚举64个本质不同的位置, 是否需要切段原点联想它的边, 复杂度电磁check复杂度64 * 64 用sosdp能优化到64 * 6 #include<bits/stdc++.h> #define LL long long #define LD long double #define ull unsigned

Codeforces 343E 最小割树

题意及思路:https://www.cnblogs.com/Yuzao/p/8494024.html 最小割树的实现参考了这篇博客:https://www.cnblogs.com/coder-Uranus/p/9771919.html 代码: #include <bits/stdc++.h> #define INF 0x3f3f3f3f using namespace std; const int maxn = 210; const int maxm = 2010; int head[maxn

漫话最小割 part1

p.p1 { margin: 0.0px 0.0px 0.0px 0.0px; font: 12.0px "PingFang SC"; color: #454545 } p.p2 { margin: 0.0px 0.0px 0.0px 0.0px; font: 13.0px "Times New Roman"; color: #2d2d2d } p.p3 { margin: 0.0px 0.0px 0.0px 0.0px; font: 12.0px "Ti

网络战争 [KD-Tree+最小割树]

题面 思路 首先吐槽一下: 这题是什么东西啊??出题人啊,故意拼题很有意思吗??还拼两个这么毒瘤的东西???? 10K代码了解一下???? 然后是正经东西 首先,本题可以理解为这样: 给定$n$个块,每个块有一个根,每个根只会主动连出去一条无向边,每次求两点最小割 那么,我们显然可以把每个块内的最小割树建立出来,同时把块的根之间的最小割树也建立出来 如果询问点在同一个块里面,显然可以直接最小割树处理 否则就是两边的点到块根的最小割和两个块根之间的最小割的最小值 所以,我们先对于所有的块根,建出K

【BZOJ2039】【2009国家集训队】人员雇佣 [最小割]

人员雇佣 Time Limit: 20 Sec  Memory Limit: 259 MB[Submit][Status][Discuss] Description 作为一个富有经营头脑的富翁,小L决定从本国最优秀的经理中雇佣一些来经营自己的公司.这些经理相互之间合作有一个贡献指数,(我们用Ei,j表示i经理对j经理的了解程度),即当经理i和经理j同时被雇佣时,经理i会对经理j做出贡献,使得所赚得的利润增加Ei,j.当然,雇佣每一个经理都需要花费一定的金钱Ai,对于一些经理可能他做出的贡献不值得