题目大意

给一颗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;
}

转载于:https://www.cnblogs.com/ZH-comld/p/10946503.html

CF786E ALT相关推荐

  1. 网络流的各种加边优化

    1.最显然的前缀优化. 2.线段树优化[CF 793G]Oleg and Chess dalao博客 3.倍增优化 CF786E ALT 最小割+倍增lca 这个dalao讲的好 AC Code:(图 ...

  2. 【学习笔记】最大权闭合子图

    对于非 DAG 的图也能用最小割求解最大权闭合子图. 理解:对于一个环,如果正点权有一个没删的话,那么整个环是可达的(相邻的边的容量为 inf),这样环上所有负全点都会向汇点断边,同时为了满足最优性, ...

  3. 设置VSCode运行任务命令快捷键Alt+R,通常用于npm start(对频繁使用该命令可节省50%的输入命令行打字时间)

    首次运行Alt+R 然后继续Enter直到运行npm start为止

  4. 设置VSCode Git签出分支快捷键Alt+G

    按住Alt+G,就可以切换分支了,灰常方便!~

  5. 设置WebStrom切换最近打开过的项目快捷键Alt+E

    Ctrl+Alt+S

  6. 设置VSCode快速切换多个项目窗口的快捷键Alt+E

    Ctrl+K Ctrl+S 搜索"切换窗口" Alt+E

  7. 设置WebStorm查看本地源码文件个人修改的历史记录快捷键Alt+Shift+H、Ctrl+Shift+H(通常用于调试bug,发现文件出问题需要回溯到若干天之前)

    Alt+Shift+H 查看整个代码文件的修改历史记录 Ctrl+Shift+H 只查看被选中代码内容的修改历史记录(更具针对性)

  8. 水平反向拆分VSCode编辑器快捷键为Ctrl+\(正交拆分Ctrl+K Ctrl+\),如何快速将当前组编辑器窗口复制到另一侧?设置垂直向下拆分编辑器快捷键Alt+\

     记住了快速按快捷键先后顺序是 当前窗口在左边编辑器组的时候: Ctrl+\ Ctrl+右箭头 当前窗口在右边编辑器组的时候: Ctrl+Alt+左箭头 Ctrl+\ Ctrl+右箭头 设置垂直向下拆 ...

  9. 设置Fetch快捷键Ctrl+Alt+Shift+1

    在提交代码之前,建议最好先Fetch代码下来(如果有冲突,系统会提示),然后再操作Merge到本地分支,这样做是为了避免有其他人同时修改了当前分支,如果直接用Ctrl+T(pull代码)极有可能覆盖本 ...

最新文章

  1. js中的失误导致的奇怪事
  2. 关于PHP Session 的配置与启动问题解决
  3. 云计算入门学习资料,linux云计算学习大纲
  4. Common Lisp语言快速入门
  5. IDEA 集成Lombok 插件-配置注解处理器
  6. 用户只有一部手机,怎么保护私钥—— mixin如何实现资产安全
  7. 3.STC15W408AS单片机GPIO
  8. 90-0004Web颜色标准【中英文颜色对照】
  9. 华为ensp OSPF单区域配置
  10. 快速在网站跳转支付宝付款链接
  11. Supervisor管理springboot应用
  12. 若依管理系统漏洞利用
  13. [Diary]毕业行程表--最后的十天
  14. *1-4 OJ 605 格雷码
  15. 【马斯克:从宇宙到人脑的征程】若AI攻占地球,猎鹰可带领人类移民火星
  16. 巨头押注的全屋智能,正在驱动海信、华为、小米们「自我革命」
  17. 洛谷——P7583 [COCI2012-2013#1] DOM(java实现)
  18. E0413: 不存在从 “std::string“ 到 “const char *“ 的适当转换函数
  19. 快手抖音上热门涨粉后怎么赚钱引流变现四大技巧-阿甘电商
  20. Docker(六)Docker Hub+Docker Registry

热门文章

  1. 如果你很迷恋一个人,那你一定配不上他。
  2. 2018年8月草原计划
  3. 带你全面掌握高级知识点!毕业一年萌新的Android大厂面经,论程序员成长的正确姿势
  4. 四级单词自编故事记忆法Week5(5)
  5. js 实现 input 框 是否只读属性
  6. 在零售门店引进自助收银设备应用对于商家有哪些用处?
  7. 今目标登录时报网络错误E110
  8. 今阶段面试笔试常见问题总结
  9. Yeoman - 搭建自己的脚手架
  10. 【NLP】自然语言处理的高级序列建模