正题

题目链接:https://www.luogu.com.cn/problem/P4606


题目大意

给出\(n\)个点\(m\)条边的一张图,\(q\)次询问给出一个点集,询问有多少个点割掉后可以是点集中至少一个点对不连通。


解题思路

就是问圆方树上的虚树中的圆点数量,照着统计就好了。

细节有点多,注意不要习惯性的把根节点带进虚树就好了。

时间复杂度\(O(n\log n)\)


code

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
#include<stack>
using namespace std;
const int N=2e5+10;
int T,n,m,q,dfc,cnt,ans,p[N],dfn[N],low[N],s[N];
int pn,dis[N],dep[N],fa[N],top[N],siz[N],son[N];
vector<int> G[N],H[N];stack<int> S;
void tarjan(int x){
dfn[x]=low[x]=++dfc;S.push(x);
for(int i=0;i<H[x].size();i++){
int y=H[x][i];
if(!dfn[y]){
tarjan(y);
low[x]=min(low[x],low[y]);
if(low[y]==dfn[x]){
int k;++n;
do{
k=S.top();S.pop();
G[n].push_back(k);
G[k].push_back(n);
}while(k!=y);
G[n].push_back(x);
G[x].push_back(n);
}
}
else low[x]=min(low[x],dfn[y]);
}
return;
}
void dfs1(int x){
dfn[x]=++dfc;siz[x]=1;
dep[x]=dep[fa[x]]+1;
dis[x]=dis[fa[x]]+(x<=pn);
for(int i=0;i<G[x].size();i++){
int y=G[x][i];
if(y==fa[x])continue;
fa[y]=x;dfs1(y);siz[x]+=siz[y];
if(siz[y]>siz[son[x]])son[x]=y;
}
return;
}
void dfs2(int x){
if(son[x]){
top[son[x]]=top[x];
dfs2(son[x]);
}
for(int i=0;i<G[x].size();i++){
int y=G[x][i];
if(y==fa[x]||y==son[x])continue;
top[y]=y;dfs2(y);
}
return;
}
int LCA(int x,int y){
while(top[x]!=top[y]){
if(dep[top[x]]<dep[top[y]])swap(x,y);
x=fa[top[x]];
}
return (dep[x]<dep[y])?x:y;
}
void Add(int x){
if(!cnt){s[++cnt]=x;return;}
int lca=LCA(x,s[cnt]);
while(cnt>1&&dep[s[cnt-1]]>dep[lca])
ans+=dis[s[cnt]]-dis[s[cnt-1]],cnt--;
if(dep[s[cnt]]>dep[lca])
ans+=dis[s[cnt]]-dis[lca],cnt--;
if(s[cnt]!=lca)s[++cnt]=lca;
s[++cnt]=x;return;
}
bool cmp(int x,int y)
{return dfn[x]<dfn[y];}
int main()
{
scanf("%d",&T);
while(T--){
scanf("%d%d",&n,&m);dfc=0;pn=n;
for(int i=1;i<=2*n;i++)
H[i].clear(),G[i].clear(),dfn[i]=low[i]=son[i]=0;
for(int i=1;i<=m;i++){
int x,y;
scanf("%d%d",&x,&y);
H[x].push_back(y);
H[y].push_back(x);
}
tarjan(1);dfc=0;
dfs1(1);top[1]=1;dfs2(1);
scanf("%d",&q);
while(q--){
scanf("%d",&m);
for(int i=1;i<=m;i++)scanf("%d",&p[i]);
sort(p+1,p+1+m,cmp);cnt=ans=0;
// if(p[1]!=1)s[++cnt]=1;
for(int i=1;i<=m;i++)
Add(p[i]);
while(cnt>1)ans+=dis[s[cnt]]-dis[s[cnt-1]],cnt--;
printf("%d\n",ans-m+(LCA(p[1],p[m])<=pn));
}
}
return 0;
}

最新文章

  1. 1.Linux是什么?
  2. 动态创建Layout
  3. 花生壳+Tomcat
  4. 百胜集团李磊:BPM实现业务流程全过程无缝链接
  5. php安装redis扩展
  6. CSS之上边栏
  7. 东芝超级本从win8到win7
  8. linux mysql默认安装在哪个目录
  9. ecshop 后台批量上传商品 完整上传
  10. Python 基金会 —— 模块和包简介
  11. angularjs promise详解
  12. MyBatis学习总结(三)——多表关联查询与动态SQL
  13. Sql分页代码示例
  14. MongoDB增删改查实例
  15. 安装xcache3.0.3/3.2,为php加速
  16. vue中svg图标使用
  17. Vs2017 控制台 中文输出是乱码的问题解决
  18. java this 子类调父类,父类再调用子类已覆盖的方法及属性(又一次理解)
  19. 同上两篇 这篇是关于shader的
  20. 【转】在android程序中使用配置文件properties

热门文章

  1. 05.SpringMVC之请求映射
  2. shiro(二)
  3. jQuery中获取属性值:attr()、html()、text()、val()等(一)
  4. servlet中servletContext的五大作用(三)
  5. springboot邮通知553错误和
  6. 二.Go微服务--令牌桶
  7. php检测数组长度的函数sizeof count
  8. 使用什么快捷键,关闭、打开、最小化qq聊天窗口
  9. React事件处理、收集表单数据、高阶函数
  10. 存储系统管理(三)——磁盘配额及lvm逻辑卷管理