CF786E ALT

Posted zh-comld

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了CF786E ALT相关的知识,希望对你有一定的参考价值。

题目大意

给一颗n个节点的树,每个边上有一个守卫。有m个居民,每个居民有一个散步路径(两个节点的树上最短路)。一个居民高兴当且仅当他获得了一个宠物或者他散步的路径上所有的守卫都有宠物。求最少需要几个宠物能让所有居民高兴。输出方案。

n,m <= 20000

输入格式:

第一行两个整数,n和m

接下来n-1行,每行两个整数,代表一条边

接下来m行,每行两个整数,代表一个居民的路径端点。

输出格式:

第一行一个数ans,表示最少的宠物数

第二行一个数p,代表给居民发的宠物数,接下来p个整数,表示获得宠物的居民

第三行一个数w,表示给守卫发的宠物数,接下来w个整数,表示获得宠物的守卫(编号为输入顺序)

题解

不难想到一个最小割模型,源点向居民连,守卫向汇点连,居民向覆盖的所有守卫连边,然后最小割。

然后倍增优化连边。

这题主要还得输出方案。

第一次遇到最小割要输出方案的。

方法是这样的,首先我们从源点开始\(dfs\)有流量的边,那么左部点一定是没有选的,\(dfs\)到的右部点一定是选了的,这时如果右部点有向左部点连的边,说明这个左部点也没选,那么继续从左部点开始点\(dfs\)

这样所有\(dfs\)到的左部点都没有被选,所有\(dfs\)到的右部点都被选了。

代码

#include<bits/stdc++.h>
#define inf 2e9
#define N 20009
#define mm make_pair
#define P pair<int,int>
using namespace std;
typedef long long ll;
int p[20][N],id[20][N],now,tot=1,dep[N],n,m,rec[N];
int head[N<<5],cur[N<<5],deep[N<<5];
bool vis[N<<5];
P re[N];
queue<int>q;
inline ll rd(){
  ll x=0;char c=getchar();bool f=0;
  while(!isdigit(c)){if(c=='-')f=1;c=getchar();}
  while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=getchar();}
  return f?-x:x;
}
vector<int>vec[N],ans1,ans2;
vector<int>::iterator it;
struct edge{
  int n,to,l;
}e[N<<6];
inline void add(int u,int v,int l){
  e[++tot].n=head[u];e[tot].to=v;head[u]=tot;e[tot].l=l;
  e[++tot].n=head[v];e[tot].to=u;head[v]=tot;e[tot].l=0;
}
inline bool bfs(int s,int t){
  memset(deep,0,sizeof(deep));
  memcpy(cur,head,sizeof(cur));
  q.push(s);deep[s]=1;
  while(!q.empty()){
    int u=q.front();q.pop();
    for(int i=head[u];i;i=e[i].n){
      int v=e[i].to;
      if(!deep[v]&&e[i].l){
        deep[v]=deep[u]+1;
        q.push(v);
      }
    }
  }
  return deep[t];
}
int dfs(int u,int t,int l){
  if(u==t||!l)return l;
  int f,flow=0;
  for(int &i=cur[u];i;i=e[i].n){
    int v=e[i].to;
    if(deep[u]+1==deep[v]&&(f=dfs(v,t,min(l,e[i].l)))){
      flow+=f;e[i].l-=f;e[i^1].l+=f;l-=f;
      if(!l)break;
    }
  }
  return flow;
}
void dfs(int u,int fa){
  for(int i=1;(1<<i)<=dep[u];++i){
    p[i][u]=p[i-1][p[i-1][u]];
    id[i][u]=++now;
    add(id[i][u],id[i-1][u],inf);
    add(id[i][u],id[i-1][p[i-1][u]],inf);
  }
  for(vector<int>::iterator it=vec[u].begin();it!=vec[u].end();++it){
    int v=*it;
    if(v==fa)continue;
    dep[v]=dep[u]+1;
    p[0][v]=u;id[0][v]=++now;
    dfs(v,u);
  }
}
inline int getlca(int u,int v){
  if(dep[u]<dep[v])swap(u,v);
  for(int i=19;i>=0;--i)if(dep[u]-(1<<i)>=dep[v])u=p[i][u];
  if(u==v)return u;
  for(int i=19;i>=0;--i)if(p[i][u]!=p[i][v]){
    u=p[i][u];v=p[i][v];
  }
  return p[0][u];
}
void ser(int u){
  vis[u]=1;
  for(int i=head[u];i;i=e[i].n){
    int v=e[i].to;
    if(!vis[v]&&e[i].l)ser(v);
  }
}
int main(){
    n=rd();m=rd();
    int x,y;
    for(int i=1;i<n;++i){
        x=rd();y=rd();
        vec[x].push_back(y);
        vec[y].push_back(x);
        re[i]=mm(x,y);
    }
    dfs(1,0);
    for(int i=1;i<=m;++i){
        x=rd();y=rd();
        ++now;add(0,now,1);
        rec[i]=now;
        int lca=getlca(x,y);
        for(int j=19;j>=0;--j){
          if(p[j][x]&&dep[p[j][x]]>=dep[lca]){
            add(now,id[j][x],inf);
            x=p[j][x];
          }
        }
        for(int j=19;j>=0;--j){
          if(p[j][y]&&dep[p[j][y]]>=dep[lca]){
            add(now,id[j][y],inf);
            y=p[j][y];
          }
        }
    }
    int ans=0;
    for(int i=2;i<=n;++i)add(id[0][i],now+1,1);
    while(bfs(0,now+1))ans+=dfs(0,now+1,inf);
    cout<<ans<<endl;
    ser(0);
    for(int i=1;i<=m;++i){
      if(!vis[rec[i]])ans1.push_back(i);
    }
    for(int i=1;i<n;++i){
      if(dep[re[i].first]<dep[re[i].second])swap(re[i].first,re[i].second);
      if(vis[id[0][re[i].first]])ans2.push_back(i);
    }
    printf("%d ",ans1.size());
    for(vector<int>::iterator it=ans1.begin();it!=ans1.end();++it)
      printf("%d ",*it);puts("");
    printf("%d ",ans2.size());
    for(vector<int>::iterator it=ans2.begin();it!=ans2.end();++it)
      printf("%d ",*it);
    return 0;
}

以上是关于CF786E ALT的主要内容,如果未能解决你的问题,请参考以下文章

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

如何从后台弹出片段

在 starUML 的序列图中使用 alt

手机怎么退出全屏模式

cf 模拟

phhstrom 快捷键