洛谷4606 SDOI2018战略游戏(圆方树+虚树)
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战略游戏(圆方树+虚树)的更多相关文章
- bzoj5315/luoguP4517 [SDOI2018]战略游戏(圆方树,虚树)
bzoj5315/luoguP4517 [SDOI2018]战略游戏(圆方树,虚树) bzoj Luogu 题目描述略(太长了) 题解时间 切掉一个点,连通性变化. 上圆方树. $ \sum |S| ...
- [SDOI2018]战略游戏 圆方树,树链剖分
[SDOI2018]战略游戏 这题是道路相遇(题解)的升级版,询问的两个点变成了\(S\)个点. LG传送门 还是先建出圆方树,考虑对于询问的\(S\)个点,答案就是圆方树上能包含这些点的最小连通块中 ...
- 洛谷P4606 [SDOI2018]战略游戏 【圆方树 + 虚树】
题目链接 洛谷P4606 双倍经验:弱化版 题解 两点之间必经的点就是圆方树上两点之间的圆点 所以只需建出圆方树 每次询问建出虚树,统计一下虚树边上有多少圆点即可 还要讨论一下经不经过根\(1\)的情 ...
- 洛谷P4606 [SDOI2018]战略游戏 [广义圆方树]
传送门 思路 先考虑两点如何使他们不连通. 显然路径上所有的割点都满足条件. 多个点呢?也是这样的. 于是可以想到圆方树.一个点集的答案就是它的虚树里圆点个数减去点集大小. 可以把点按dfs序排序,然 ...
- BZOJ5329:[SDOI2018]战略游戏(圆方树,虚树)
Description 省选临近,放飞自我的小Q无心刷题,于是怂恿小C和他一起颓废,玩起了一款战略游戏. 这款战略游戏的地图由n个城市以及m条连接这些城市的双向道路构成,并且从任意一个城市出发总能沿着 ...
- Luogu4606 SDOI2018 战略游戏 圆方树、虚树、链并
传送门 弱化版 考虑到去掉一个点使得存在两个点不连通的形式类似割点,不难想到建立圆方树.那么在圆方树上对于给出的关键点建立虚树之后,我们需要求的就是虚树路径上所有圆点的数量减去关键点的数量. 因为没有 ...
- Luogu P4606 [SDOI2018] 战略游戏 圆方树 虚树
https://www.luogu.org/problemnew/show/P4606 把原来的图的点双联通分量缩点(每个双联通分量建一个点,每个割点再建一个点)(用符合逻辑的方式)建一棵树(我最开始 ...
- BZOJ.5329.[SDOI2018]战略游戏(圆方树 虚树)
题目链接 显然先建圆方树,方点权值为0圆点权值为1,两点间的答案就是路径权值和减去起点终点. 对于询问,显然可以建虚树.但是只需要计算两关键点间路径权值,所以不需要建出虚树.统计DFS序相邻的两关键点 ...
- 洛谷P4630 铁人两项--圆方树
一道很好的圆方树入门题 感谢PinkRabbit巨佬的博客,讲的太好啦 首先是构建圆方树的代码,也比较好想好记 void tarjan(int u) { dfn[u] = low[u] = ++dfn ...
随机推荐
- jwt《token》
payload与claims只能存在一个这部分是jwt源码:依赖如下:官方文档的依赖 <dependency> <groupId>io.jsonwebtoken</gro ...
- 整型:int
整型:int 整型变量的定义和输出 注意://short<=int<=long<=longlong 代码示例一: #include<stdio.h> int ma ...
- grpc基础
RPC 框架原理 RPC 框架的目标就是让远程服务调用更加简单.透明,RPC 框架负责屏蔽底层的传输方式(TCP 或者 UDP).序列化方式(XML/Json/ 二进制)和通信细节.服务调用者可以像调 ...
- Linux 安装 Harbor 私有镜像仓库
下载 最新发行:https://github.com/goharbor/harbor/releases # 下载文件 wget https://github.com/goharbor/harbor/r ...
- 哲学家就餐问题-Java语言实现死锁避免
哲学家就餐问题-Java语言实现死锁避免 我死锁预防是至少破坏死锁产生的四个必要条件之一,带来的问题就是系统资源利用率低且不符合开发习惯,而死锁避免不是事先釆取某种限制措施破坏死锁的必要条件,只是注意 ...
- K8S资源编排(yaml)
1.yaml的格式 2.yaml的组成部分 3.yaml常用字段的含义 4.yaml编写方式 (1)方式一:使用kubectl create命令生成yaml文件,然后修改 (2)方式2:在已经部署好的 ...
- IPsec 9个包分析(主模式+快速模式)
第一阶段:ISAKMP协商阶段 1.1 第一包 包1:发起端协商SA,使用的是UDP协议,端口号是500,上层协议是ISAKMP,该协议提供的是一个框架,里面的负载Next payload类似模块,可 ...
- 枚举类enum
一.枚举类 package com.xxx.xf.common.enums; import com.xxx.xf.workday.contant.HolidayContant; /** * @Auth ...
- CUDA 矩阵乘法终极优化指南
作者:马骏 | 旷视 MegEngine 架构师 前言 单精度矩阵乘法(SGEMM)几乎是每一位学习 CUDA 的同学绕不开的案例,这个经典的计算密集型案例可以很好地展示 GPU 编程中常用的优化技巧 ...
- ecshop 首页调用指定分类下的销售排行
/*首页调用指定分类下的销售排行*/ function get_cats_top10($cat = '') { $sql = 'SELECT cat_id, cat_name ' . 'FROM ' ...