P4606 [SDOI2018]战略游戏

【题意】

给出一个无向图,q次询问,每次给定一个点集s代表占领点,问有多少个未被占领的点可以作为点集s中两个点u,v的割点

【分析】

首先,先建立圆方树,问题转化为能包含 给定点集 的最小连通块的 圆点个数 - 占领点个数,也就是点集中两两点的并集的点个数-占领点个数

然后,按照圆方树的套路,我们要给点赋值,显然圆点赋1,方点赋0即可,然后把点权转移到父亲边的边权上去。

接着,我们需要利用类似虚树的套路,也算是一个小技巧,将询问点集按照dfs序排序,相邻两两简单路径长度之和+首尾简单路径长度(也就是围成一个圆,相邻两人的距离之和)除以二即可

那么最终答案为记得加上根节点(首尾的lca)的点权,这是点权转化为边权中丢失的部分

【代码】

#include
using namespace std;
typedef long long ll;
const int maxn=2e5+5;
vector  G[maxn],YFT[maxn]; 
int n,m;
int dfn[maxn],low[maxn],dfs_time;
int in[maxn],st[maxn],top;
int sq;
void add(int x,int y)
{
    YFT[x].push_back(y);
}
void tarjan(int u,int fa)
{
    dfn[u]=low[u]=++dfs_time;
    in[u]=1; st[++top]=u;
    for(auto to:G[u])
    {
        if(to==fa || dfn[to]>dfn[u]) continue;
        if(!dfn[to])
        {
            tarjan(to,u);
            low[u]=min(low[u],low[to]);
        }
        else if(in[to]) {low[u]=min(low[u],dfn[to]); continue; }
        if(low[to]==dfn[u])
        {
            sq++;
            add(u,sq);
            while(st[top]!=to)
            {
                in[st[top]]=0; add(sq,st[top]);
                top--;
            }
            add(sq,to); in[to]=0; top--;   
        }
        else if(low[to]>dfn[u])
        {
            add(u,to); top--; in[to]=0;
        }
    }
}
int dep[maxn],dis[maxn],rk;
int f[maxn][19];
void dfs(int u,int fa)
{
    dfn[u]=++rk;
    for(auto to:YFT[u])
    {
        // if(to==fa) continue;
        dep[to]=dep[u]+1; dis[to]=dis[u]+(to<=n);
        f[to][0]=u;
        for(int i=1;i<=18;i++) f[to][i]=f[f[to][i-1]][i-1];
        dfs(to,u);
    }
}
int getlca(int x,int y)
{
    if(dep[x]=0;i--)
        if(dep[f[x][i]]>=dep[y])
            x=f[x][i];
    if(x==y) return x;
    for(int i=18;i>=0;i--)
        if(f[x][i]!=f[y][i])
            x=f[x][i],y=f[y][i];
    return f[x][0];
}
int getdis(int x,int y)
{
    return dis[x]+dis[y]-2*dis[getlca(x,y)];
}
int qu[maxn];
bool cmp(int x,int y)
{
    return dfn[x]

你可能感兴趣的:(算法,圆方树)