正题

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


题目大意

给出一张无向图满足若\(x\Rightarrow z,y\Rightarrow z\)那么有\(x\Rightarrow y\)或\(y\Rightarrow x\)。

\(q\)次询问给出起点和终点和\(k\)条临时的边,求可能经过点的数量

\(1\leq n,q\leq 3\times 10^5,0\leq k\leq 2\)


解题思路

那个条件就是说我们缩点之后可以找出一个联通性和原图相同的树。

先缩点然后和原图相同的树的话我们用拓扑排序找,让每个点入栈的那个点就是它的父节点。

然后因为最多加两条边所以我们可以把有影响的点找出来构成一棵虚树然后暴力跑出答案就好了。

时间复杂度\(O(n+Qk)\)


code

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
#include<stack>
#include<queue>
#include<cctype>
#include<iostream>
using namespace std;
const int N=3e5+10,Z=18;
struct node{
int to,next,w;
}a[N];
int n,m,Q,k,dcc,cnt,dfr,top,tot,clt,ans,num,p[7];
int dfn[N],low[N],v[N],ls[N],rt[N],cl[N],dep[N];
int f[N][Z+1],col[N],siz[N],in[N],st[N],dis[N],ed[N];
bool ins[N];stack<int>S;queue<int>q;
vector<int>G[N],F[N],T[N],D[N];
inline char Getchar()
{
static char buf[100000],*p1=buf+100000,*pend=buf+100000;
if(p1==pend)
{
p1=buf; pend=buf+fread(buf,1,100000,stdin);
if (pend==p1) return -1;
}
return *p1++;
}
inline int read()
{
char c;int d=1;int f=0;
while(c=Getchar(),!isdigit(c))if(c==45)d=-1;f=(f<<3)+(f<<1)+c-48;
while(c=Getchar(),isdigit(c)) f=(f<<3)+(f<<1)+c-48;
return d*f;
}
inline void tarjan(int x){
dfn[x]=low[x]=++cnt;
ins[x]=1;S.push(x);
for(int i=0;i<G[x].size();i++){
int y=G[x][i];
if(!dfn[y]){
tarjan(y);
low[x]=min(low[x],low[y]);
}
else if(ins[y])
low[x]=min(low[x],dfn[y]);
}
if(low[x]==dfn[x]){
++dcc;
while(S.top()!=x){
col[S.top()]=dcc;
ins[S.top()]=0;
siz[dcc]++;S.pop();
}
col[x]=dcc;ins[x]=0;
siz[dcc]++;S.pop();
}
return;
}
inline void Topsort(){
for(int i=1;i<=dcc;i++)
if(!in[i])rt[i]=i,q.push(i);
while(!q.empty()){
int x=q.front();q.pop();
for(int i=0;i<F[x].size();i++){
int y=F[x][i];in[y]--;
if(!in[y]){
q.push(y);
T[x].push_back(y);
}
}
}
return;
}
inline void dfs(int x,int fa){
dis[x]=dis[fa]+siz[x];
dep[x]=dep[fa]+1;dfn[x]=++dfr;
for(int i=0;i<T[x].size();i++){
int y=T[x][i];
if(y==fa)continue;
f[y][0]=x;rt[y]=rt[x];
dfs(y,x);
}
ed[x]=dfr;return;
}
inline int LCA(int x,int y){
if(dep[x]>dep[y])swap(x,y);
for(int i=Z;i>=0;i--)
if(dep[f[y][i]]>=dep[x])y=f[y][i];
if(x==y)return x;
for(int i=Z;i>=0;i--)
if(f[x][i]!=f[y][i])x=f[x][i],y=f[y][i];
return f[x][0];
}
inline bool cmp(int x,int y)
{return dfn[x]<dfn[y];}
inline void addl(int x,int y,int w){
a[++tot].to=y;
a[tot].next=ls[x];
a[tot].w=(dis[y]-dis[x]-siz[y])*w;
D[y].push_back(x);ls[x]=tot;return;
}
inline void Add(int x){
if(rt[st[top]]!=rt[x]){
while(top>1)addl(st[top-1],st[top],1),top--;
if(x!=rt[x])st[top]=rt[x],cl[++clt]=x;
}
if(!top){st[++top]=x;cl[++clt]=x;return;}
int lca=LCA(st[top],x);
while(top>1&&dep[st[top-1]]>dep[lca])
addl(st[top-1],st[top],1),top--;
if(dep[st[top]]>dep[lca])addl(lca,st[top],1),top--;
if((!top)||(st[top]!=lca))st[++top]=lca,cl[++clt]=lca;
st[++top]=x;cl[++clt]=x;
}
inline void mark(int x){
v[x]=1;
for(int i=0;i<D[x].size();i++)
if(!v[D[x][i]])
mark(D[x][i]);
return;
}
inline void calc(int x){
if(v[x])ans+=siz[x];v[x]|=2;
for(int i=ls[x];i;i=a[i].next){
int y=a[i].to;
if(v[y]&1)ans+=a[i].w,a[i].w=0;
if((v[y]&2)||!(v[y]&1))continue;
calc(y);
}
return;
}
signed main()
{
freopen("P7737_12.in","r",stdin);
freopen("data.out","w",stdout);
n=read();m=read();Q=read();k=read();
for(int i=1;i<=m;i++){
int x=read(),y=read();
G[x].push_back(y);
}
for(int i=1;i<=n;i++)
if(!dfn[i])tarjan(i);
for(int x=1;x<=n;x++)
for(int i=0;i<G[x].size();i++){
int y=G[x][i];
if(col[x]==col[y])continue;
F[col[x]].push_back(col[y]);in[col[y]]++;
}
Topsort();n=dcc;
for(int i=1;i<=n;i++)
if(rt[i]==i)dfs(rt[i],0);
for(int j=1;j<=Z;j++)
for(int i=1;i<=n;i++)
f[i][j]=f[f[i][j-1]][j-1];
while(Q--){
int s=read(),t=read();
s=col[s];t=col[t];
p[1]=s;p[2]=t;num=2;
tot=clt=top=ans=0;
for(int i=1;i<=k;i++){
int x=read(),y=read();
x=col[x];y=col[y];
p[++num]=x;p[++num]=y;
addl(x,y,0);
}
sort(p+1,p+1+num,cmp);
num=unique(p+1,p+1+num)-p-1;
for(int i=1;i<=num;i++)Add(p[i]);
while(top>1)addl(st[top-1],st[top],1),top--;
mark(t);calc(s);
cout<<ans<<'\n';
for(int i=1;i<=clt;i++){
ls[cl[i]]=v[cl[i]]=0;
if(D[cl[i]].size())D[cl[i]].clear();
}
}
return 0;
}

P7737-[NOI2021]庆典【tarjan,虚树】的更多相关文章

  1. [Codeforces 639F] Bear and Chemistry (Tarjan+虚树)(有详细注释)

    [Codeforces 639F] Bear and Chemistry(Tarjan+虚树) 题面 给出一个n个点,m条边的无向图(不保证连通,可能有自环和重边),有q次询问,每次询问给出p个点和q ...

  2. 图论杂项细节梳理&模板(虚树,圆方树,仙人掌,欧拉路径,还有。。。)

    orzYCB 虚树 %自为风月马前卒巨佬% 用于优化一类树形DP问题. 当状态转移只和树中的某些关键点有关的时候,我们把这些点和它们两两之间的LCA弄出来,以点的祖孙关系连成一棵新的树,这就是虚树. ...

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

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

  4. 仙人掌 && 圆方树 && 虚树 总结

    仙人掌 && 圆方树 && 虚树 总结 Part1 仙人掌 定义 仙人掌是满足以下两个限制的图: 图完全联通. 不存在一条边处在两个环中. 其中第二个限制让仙人掌的题做 ...

  5. Codechef Sad Pairs——圆方树+虚树+树上差分

    SADPAIRS 删点不连通,点双,圆方树 非割点:没有影响 割点:子树DP一下 有不同颜色,所以建立虚树 在圆方树上dfs时候 如果当前点是割点 1.统计当前颜色虚树上的不连通点对,树形DP即可 2 ...

  6. 算法笔记--树的直径 && 树形dp && 虚树 && 树分治 && 树上差分 && 树链剖分

    树的直径: 利用了树的直径的一个性质:距某个点最远的叶子节点一定是树的某一条直径的端点. 先从任意一顶点a出发,bfs找到离它最远的一个叶子顶点b,然后再从b出发bfs找到离b最远的顶点c,那么b和c ...

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

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

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

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

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

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

随机推荐

  1. Java使用Lettuce操作redis

    maven包 # 包含了lettuce jar <dependency> <groupId>org.springframework.boot</groupId> & ...

  2. Java常用类之时间类

    JDK8之前日期时间API 1. java.lang.System类 2. java.util.Date类 3. java.text.SimpleDateFormat类 4. java.util.Ca ...

  3. Object 的wait()方法

    The java.lang.Object.wait() causes current thread to wait until another thread invokes the notify() ...

  4. mzy对于枚举的理解

    关于enum,其实就是简化了的class,功能就是提供一个个独立的.特定含义的常量! 在JDK5.0之前我们想模拟enum的功能,只能使用自定义类的形式: 1.首先私有化构造方法,让外部不能new对象 ...

  5. 关于mysql的备份和恢复

    备份:在登录之前(cmd中)mysqldump -u root -p [数据库名称] > c:/back.sql备份的话,肯定是DBA才能做,所以只能用root:恢复mysql -u root ...

  6. 分数化循环小数C++实现

    引言 前一阵做了一个有理数四则混合运算的程序(详见:用C++实现的有理数(分数)四则混合运算计算器),以分数形式呈现运算结果.这次添加以循环小数形式呈现运算结果的功能.例如: Please input ...

  7. 如何实现CSS限制字数,超出部份显示省略号

    <div style="width:200px; white-space:nowrap;overflow:hidden;text-overflow:ellipsis; border:1 ...

  8. Linux学习笔记 - Linux快捷操作及常用命令

    一.快捷键 剪切光标前的内容 Ctrl + u 剪切光标至行末的内容 Ctrl + k 粘贴 Ctrl + u 或 Ctrl +k 的内容 Ctrl + y 移动光标到行末 Ctrl + e 移动光标 ...

  9. NOIP模拟13「工业题·卡常题·玄学题」

    T1:工业题 基本思路   这题有一个重要的小转化: 我们将原来的函数看作一个矩阵,\(f(i,j-1)*a\)相当于从\(j-1\)向右走一步并贡献a,\(f(i-1,j)*b\)相当于从\(i-1 ...

  10. mysql远程连接以及错误解决&命令行基本操作

    现在大家的程序服务基本都是部署在云服务器上,今天我分享记录一下:使用mysql数据库过程中比较常见操作和遇到的问题 环境:lunix 系统(阿里云服务器,华为云服务器,腾讯云等均适用) + mysql ...