洛谷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 ...
随机推荐
- Mysql时间戳转Java时间戳
MySQL 时间戳和Java返回的时间戳是不一样的 例如: 当前时间是 2014-08-04 10:42:55.204000 使用mysql时间戳函数UNIX_TIMESTAMP 返回的结果为: 14 ...
- 使用 antd 的 form 组件来自定义提交的数据格式
最近使用antd UI 的表单提交数据,数据里面有的是数组,有的是对象.提交的时候还要去校验参数,让人非常头疼.在我仔细看完文档之后,发现 antd 的 form 组件做的非常不错,这些需求通通不是问 ...
- 对于MySQL远程连接中出现的一个问题总结
2021年9月3日更新补充 (真的心累,本来是个小问题,但是网上帖子都基本差不多,基本都是相同的操作,导致搜了半个多小时才解决) 一.首先为什么要重新发一次呢,因为我发现上次写的这个记录是不完善甚至是 ...
- 剑指 Offer 31. 栈的压入、弹出序列
剑指 Offer 31. 栈的压入.弹出序列 输入两个整数序列,第一个序列表示栈的压入顺序,请判断第二个序列是否为该栈的弹出顺序.假设压入栈的所有数字均不相等.例如,序列 {1,2,3,4,5} 是某 ...
- Jenkins(6)- 新建用户
如果想从头学起Jenkins的话,可以看看这一系列的文章哦 https://www.cnblogs.com/poloyy/category/1645399.html 进入用户管理 点击新建用户 填写新 ...
- 如何高效掌控K8s资源变化?K8s Informer实现机制浅析
作者 王成,腾讯云研发工程师,Kubernetes contributor,从事数据库产品容器化.资源管控等工作,关注 Kubernetes.Go.云原生领域. 概述 进入 K8s 的世界,会发现有很 ...
- LVS负载均衡集群--DR模式部署
目录: 一.LVS-DR数据包流向分析 二.DR 模式的特点 三.LVS-DR中的ARP问题 四.DR模式 LVS负载均衡群集部署 一.LVS-DR数据包流向分析 1.为方便进行原理分析,将clien ...
- Java日期时间API系列42-----一种高效的中文日期格式化和解析方法
中文日期(2021年09月11日 和 二〇二一年九月十一日 )在生活中经常用到,2021年09月11日很好处理直接使用模板:yyyy年MM月dd日:二〇二一年九月十一日比较不好处理,需要每个数字进行转 ...
- python字典时间日期
字典时间日期 学习完本篇,你将会深入掌握 字典操作. 1.如何新建一个字典 2.如何正序以及反序访问字典 3.如何更新字典元素 4.如何删除字典 日期时间操作 1.如何打印一个2021-9-20 17 ...
- Linux系列(25) - 常用快捷键(未更新完)
快捷键 说明 Ctrl+L 清屏 tab tab按一次自动补全目录文件名称/tab按二次将目录下带有补全前面字段的所有文件目录展示出来,例子: cd / tab键按两次将根目录下所有文件展示出来 ...