QWQ深受其害

当时在现场是真的绝望......

现在再重新来看这个题

QWQ

根据题目所说,我们可以发现,对于每一个集合中的节点,我们实际上就是要求两两路径上的割点的数目

考虑到又是关于点双的题目,而且在图上,我们并没有很好的办法去做。

这时候就要考虑建出来圆方树,然后我们对于圆方树 的每个点,维护他到根的路径上的圆点个数

那么,我们该怎么求两两路径的割点总数呢(一看到数据范围,就想到虚树了啊)

冷静分析一下,发现真的直接把虚树中的点弄出来就是合法的,因为两两的路径一定会通过\(lca\),而建出来虚树,正好只会保留有用的边。

那么我们对于虚树上的两个相连的点,他们的边权的就是两个点到根的圆点个数的差。

这里有一个关于虚树的

奇技淫巧

为了忽略\(1\)号节点对答案的影响,我们可以直接选择把所有关键点的\(lca\)放到虚树的栈里面当第一个元素,而不是1

最后对于一次询问,我们只需要求虚树的边权和即可(还需要特判根的问题)

// luogu-judger-enable-o2
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<queue>
#include<map>
#include<set>
#define mk makr_pair
#define ll long long
using namespace std;
inline int read()
{
int x=0,f=1;char ch=getchar();
while (!isdigit(ch)) {if (ch=='-') f=-1;ch=getchar();}
while (isdigit(ch)) {x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}
return x*f;
}
const int maxn = 4e5+1e2;
const int maxm = 2*maxn;
int point[maxn],nxt[maxm],to[maxm];
int point1[maxn],nxt1[maxm],to1[maxm];
int cnt,cnt1;
int n,m;
int dfn[maxn],deep[maxn],low[maxn];
int f[maxn][20];
int st[maxn],tot,top;
int num;
int a[maxn],k;
int dnf[maxn],size[maxn];
void addedge1(int x,int y)
{
nxt1[++cnt1]=point1[x];
to1[cnt1]=y;
point1[x]=cnt1;
}
void addedge(int x,int y)
{
nxt[++cnt]=point[x];
to[cnt]=y;
point[x]=cnt;
}
void tarjan(int x,int fa)
{
dfn[x]=low[x]=++tot;
st[++top]=x;
for (int i=point1[x];i;i=nxt1[i])
{
int p=to1[i];
if (p==fa) continue;
if (!dfn[p])
{
tarjan(p,x);
low[x]=min(low[x],low[p]);
if (low[p]>=dfn[x])
{
num++;
addedge(num,x);
addedge(x,num);
do{
addedge(num,st[top]);
addedge(st[top],num);
top--;
}while (st[top+1]!=p);
}
}
else low[x]=min(low[x],dfn[p]);
}
}
int tmp;
void dfs(int x,int fa,int dep)
{
int now=0;
if (x<=n) now++;
deep[x]=dep;
dnf[x]=++tmp;
size[x]=size[fa]+now;
for (int i=point[x];i;i=nxt[i])
{
int p = to[i];
if(p==fa) continue;
f[p][0]=x;
dfs(p,x,dep+1);
}
}
void init()
{
for (int j=1;j<=19;j++)
for (int i=1;i<=num;i++)
{
f[i][j]=f[f[i][j-1]][j-1];
}
}
int go_up(int x,int d)
{
for (int i=0;i<=19;i++)
{
if ((1<<i) & d) x=f[x][i];
}
return x;
}
int lca(int x,int y)
{
if(deep[x]>deep[y]) x=go_up(x,deep[x]-deep[y]);
else y=go_up(y,deep[y]-deep[x]);
if (x==y) return x;
for (int i=19;i>=0;i--)
{
if (f[x][i]!=f[y][i])
{
x=f[x][i];
y=f[y][i];
}
}
return f[x][0];
}
bool cmp(int a,int b)
{
return dnf[a]<dnf[b];
}
struct xvtree{
int point[maxn],nxt[maxm],to[maxm];
int val[maxm];
int cnt;
int s[maxn],top;
int sum;
void init()
{
cnt=0;
sum=0;
}
void addedge(int x,int y,int w)
{
//cout<<x<<" ** "<<y<<" "<<w<<endl;
nxt[++cnt]=point[x];
to[cnt]=y;
val[cnt]=w;
point[x]=cnt;
}
void dfs(int x,int fa)
{
for (int &i=point[x];i;i=nxt[i])
{
int p = to[i];
if (p==fa) continue;
sum+=val[i];
dfs(p,x);
}
}
int solve()
{
init();
top=1;
sort(a+1,a+1+k,cmp);
int root=a[1];
for (int i=2;i<=k;i++) root=lca(root,a[i]);
s[top]=root;
for (int i=1;i<=k;i++)
{
int l = lca(s[top],a[i]);
if (l!=s[top])
{
while (top>1)
{
if (dnf[s[top-1]]>dnf[l])
{
addedge(s[top-1],s[top],size[s[top]]-size[s[top-1]]);//size表示他在圆方树上和根的路径上的圆点个数
top--;
}
else
{
if (dnf[s[top-1]]==dnf[l])
{
addedge(s[top-1],s[top],size[s[top]]-size[s[top-1]]);
top--;
break;
}
else
{
addedge(l,s[top],size[s[top]]-size[l]);
s[top]=l;
break;
}
}
}
}
if (s[top]!=a[i]) s[++top]=a[i];
}
while (top>1)
{
addedge(s[top-1],s[top],size[s[top]]-size[s[top-1]]);
top--;
}
dfs(root,0);
if(root<=n) sum++;
return sum;
}
};
xvtree xv;
int t;
int main()
{
t=read();
while (t--)
{
tmp=0;tot=0;top=0;
cnt=0;cnt1=0;
xv.init();
memset(point,0,sizeof(point));
memset(point1,0,sizeof(point1));
memset(f,0,sizeof(f));
memset(dfn,0,sizeof(dfn));
memset(low,0,sizeof(low));
memset(dnf,0,sizeof(dnf));
n=read(),m=read();
num=n;
for (int i=1;i<=m;i++)
{
int x=read(),y=read();
addedge1(x,y);
addedge1(y,x);
}
for (int i=1;i<=n;i++)
{
if (!dfn[i]) tarjan(i,0);
}
dfs(1,0,1);
init();
int q=read();
for (int i=1;i<=q;i++)
{
k=read();
for (int j=1;j<=k;j++) a[j]=read();
xv.init();
cout<<xv.solve()-k<<"\n";
}
}
return 0;
}

洛谷4606 SDOI2018战略游戏(圆方树+虚树)的更多相关文章

  1. bzoj5315/luoguP4517 [SDOI2018]战略游戏(圆方树,虚树)

    bzoj5315/luoguP4517 [SDOI2018]战略游戏(圆方树,虚树) bzoj Luogu 题目描述略(太长了) 题解时间 切掉一个点,连通性变化. 上圆方树. $ \sum |S| ...

  2. [SDOI2018]战略游戏 圆方树,树链剖分

    [SDOI2018]战略游戏 这题是道路相遇(题解)的升级版,询问的两个点变成了\(S\)个点. LG传送门 还是先建出圆方树,考虑对于询问的\(S\)个点,答案就是圆方树上能包含这些点的最小连通块中 ...

  3. 洛谷P4606 [SDOI2018]战略游戏 【圆方树 + 虚树】

    题目链接 洛谷P4606 双倍经验:弱化版 题解 两点之间必经的点就是圆方树上两点之间的圆点 所以只需建出圆方树 每次询问建出虚树,统计一下虚树边上有多少圆点即可 还要讨论一下经不经过根\(1\)的情 ...

  4. 洛谷P4606 [SDOI2018]战略游戏 [广义圆方树]

    传送门 思路 先考虑两点如何使他们不连通. 显然路径上所有的割点都满足条件. 多个点呢?也是这样的. 于是可以想到圆方树.一个点集的答案就是它的虚树里圆点个数减去点集大小. 可以把点按dfs序排序,然 ...

  5. BZOJ5329:[SDOI2018]战略游戏(圆方树,虚树)

    Description 省选临近,放飞自我的小Q无心刷题,于是怂恿小C和他一起颓废,玩起了一款战略游戏. 这款战略游戏的地图由n个城市以及m条连接这些城市的双向道路构成,并且从任意一个城市出发总能沿着 ...

  6. Luogu4606 SDOI2018 战略游戏 圆方树、虚树、链并

    传送门 弱化版 考虑到去掉一个点使得存在两个点不连通的形式类似割点,不难想到建立圆方树.那么在圆方树上对于给出的关键点建立虚树之后,我们需要求的就是虚树路径上所有圆点的数量减去关键点的数量. 因为没有 ...

  7. Luogu P4606 [SDOI2018] 战略游戏 圆方树 虚树

    https://www.luogu.org/problemnew/show/P4606 把原来的图的点双联通分量缩点(每个双联通分量建一个点,每个割点再建一个点)(用符合逻辑的方式)建一棵树(我最开始 ...

  8. BZOJ.5329.[SDOI2018]战略游戏(圆方树 虚树)

    题目链接 显然先建圆方树,方点权值为0圆点权值为1,两点间的答案就是路径权值和减去起点终点. 对于询问,显然可以建虚树.但是只需要计算两关键点间路径权值,所以不需要建出虚树.统计DFS序相邻的两关键点 ...

  9. 洛谷P4630 铁人两项--圆方树

    一道很好的圆方树入门题 感谢PinkRabbit巨佬的博客,讲的太好啦 首先是构建圆方树的代码,也比较好想好记 void tarjan(int u) { dfn[u] = low[u] = ++dfn ...

随机推荐

  1. freeswitch新增模块

    概述 freeswitch的架构由稳定的核心模块和大量的外围插件式模块组成.核心模块保持稳定,外围模块可以动态的加载/卸载,非常灵活方便. 外围模块通过核心提供的 Public API与核心进行通信, ...

  2. 证明n个正数的算术平均数不小于它们的几何平均数

  3. TCP连接中的状态

    1. 正常状态转换 我们用图 3-13 来显示在正常的 TCP 连接的建立与终止过程中,客户与服务器所经历的不同状态.读者可以对照图 3-12 来阅读,使用图 3-12 的状态图来跟踪图 3-13 的 ...

  4. 性能测试工具JMeter 基础(一)—— 安装、配置环境变量

    JMeter下载 下载地址:https://jmeter.apache.org/download_jmeter.cgi 下载完成后解压后可直接使用,不用进行安装 环境变量配置 新增变量名:JMETER ...

  5. .Net性能调优-ArrayPool

    定义 高性能托管数组缓冲池,可重复使用,用租用空间的方式代替重新分配数组空间的行为 好处 可以在频繁创建和销毁数组的情况下提高性能,减少垃圾回收器的压力 使用 获取缓冲池实例:Create/Share ...

  6. outerHTML和outerText的赋值是异步的

    用JavaScript操作DOM时,经常有生成复杂HTML结构的需求.此时,通常不是用标准DOM接口(如createElement().setAttribute().append()等)来语句式地生成 ...

  7. 如何将 Ubuntu 版本升级到新版本

    @ 目录 0.将 Ubuntu 版本升级到新版本的注意事项 1.以图形方式升级到 Ubuntu 20.04(适用于桌面用户) 2.使用命令行升级到 Ubuntu 21.10 本教程通过从 Ubuntu ...

  8. 【JDK】分析 String str=““ 与 new String()

    一.基础概念 为了讲清楚他们的差异,这里先介绍几个概念. 1.1 常量池 所谓常量池:顾名思义就是用来存放一些常量的.该常量是在编译期被确定,并被保存在已编译的.class文件中,其中包括了类,方法, ...

  9. Python中with...as...的用法详解

    简介 with是从Python2.5引入的一个新的语法,它是一种上下文管理协议,目的在于从流程图中把 try,except 和finally 关键字和资源分配释放相关代码统统去掉,简化try-.exc ...

  10. IP 地址无效化

    给你一个有效的 IPv4 地址 address,返回这个 IP 地址的无效化版本. 所谓无效化 IP 地址,其实就是用 "[.]" 代替了每个 ".". 示例 ...