【BZOJ5329】战略游戏(SDOI2018)-圆方树+虚树

测试地址:战略游戏
做法:本题需要用到圆方树+虚树。
显而易见的是,两个点之间路径的必经点,就等于它们之间路径上的所有割点。因此我们很快想到建出圆方树,这样两点间路径上所有的圆点(除去两端)就是对应的割点。而询问一个集合,问能切开集合中某两个点的所有点,那就是求所有这些点两两之间路径的并,答案就等于并集中圆点的数目减去询问集合的大小(因为询问集合内的点不能算作答案)。
树上一个点集两两之间路径的并也是一棵树,用更加熟悉的一个词来说,是一棵虚树,因此我们在圆方树上求虚树即可,时间复杂度为 O(|S|log|S|) O ( ∑ | S | log ⁡ ∑ | S | )
以下是本人代码:

#include <bits/stdc++.h>
using namespace std;
const int N=200010;
int T,n,m,q;
int first[N],firstt[N],tot,totpbc;
int low[N],dfn[N],st[N],top,tim,pos[N],p[N<<1],s;
int fa[N][20],dep[N],dis[N];
bool vis[N];
struct edge
{
    int v,next;
}e[N<<1],t[N<<1];

void insert(int a,int b)
{
    e[++tot].v=b;
    e[tot].next=first[a];
    first[a]=tot;
}

void insertt(int a,int b)
{
    t[++tot].v=b;
    t[tot].next=firstt[a];
    firstt[a]=tot;
}

void tarjan(int v,int fa)
{
    low[v]=dfn[v]=++tim;
    st[++top]=v;
    vis[v]=1;
    for(int i=first[v];i;i=e[i].next)
        if (e[i].v!=fa)
        {
            if (!vis[e[i].v])
            {
                tarjan(e[i].v,v);
                low[v]=min(low[v],low[e[i].v]);
                if (low[e[i].v]>=dfn[v])
                {
                    ++totpbc;
                    insertt(v,totpbc);
                    do
                    {
                        insertt(totpbc,st[top]);
                    }while(st[top--]!=e[i].v);
                }
            }
            else low[v]=min(low[v],dfn[e[i].v]);
        }
}

void dfs(int v)
{
    pos[v]=++tim;
    for(int i=firstt[v];i;i=t[i].next)
    {
        fa[t[i].v][0]=v;
        dep[t[i].v]=dep[v]+1;
        dis[t[i].v]=dis[v]+(t[i].v<=n);
        dfs(t[i].v);
    }
}

bool cmp(int a,int b)
{
    return pos[a]<pos[b];
}

int lca(int x,int y)
{
    if (dep[x]<dep[y]) swap(x,y);
    for(int i=18;i>=0;i--)
        if (dep[fa[x][i]]>=dep[y]) x=fa[x][i];
    if (x==y) return x;
    for(int i=18;i>=0;i--)
        if (fa[x][i]!=fa[y][i])
            x=fa[x][i],y=fa[y][i];
    return fa[x][0];
}

void build()
{
    sort(p+1,p+s+1,cmp);
    for(int i=1;i<s;i++)
        p[s+i]=lca(p[i],p[i+1]);
    sort(p+1,p+(s<<1),cmp);

    int tot=1;
    for(int i=1;i<(s<<1)-1;i++)
        if (p[i]!=p[i+1]) p[++tot]=p[i+1];

    int ans;
    top=ans=0;
    for(int i=1;i<=tot;i++)
    {
        while(top&&lca(st[top],p[i])!=st[top]) top--;
        st[++top]=p[i];
        if (top>1) ans+=dis[st[top]]-dis[st[top-1]];
    }
    if (p[1]<=n) ans++;
    printf("%d\n",ans-s);
}

int main()
{
    scanf("%d",&T);
    while(T--)
    {
        memset(first,0,sizeof(first));
        memset(firstt,0,sizeof(firstt));
        tot=0;

        scanf("%d%d",&n,&m);
        for(int i=1;i<=m;i++)
        {
            int a,b;
            scanf("%d%d",&a,&b);
            insert(a,b),insert(b,a);
        }

        tot=top=tim=0,totpbc=n;
        memset(vis,0,sizeof(vis));
        tarjan(1,0);

        memset(fa,0,sizeof(fa));
        tim=0;
        dis[1]=1,dep[1]=0,dep[0]=-1;
        dfs(1);
        for(int i=1;i<=18;i++)
            for(int j=1;j<=totpbc;j++)
                fa[j][i]=fa[fa[j][i-1]][i-1];

        scanf("%d",&q);
        for(int i=1;i<=q;i++)
        {
            scanf("%d",&s);
            for(int j=1;j<=s;j++)
                scanf("%d",&p[j]);
            build();
        }
    }

    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
题目描述 有一个 $n$ 个点的棋盘,每个点上有一个数字 $a_i$,你需要从 $(1,1)$ 走到 $(n,n)$,每次只能往右或往下走,每个格子只能经过一次,路径上的数字和为 $S$。定义一个点 $(x,y)$ 的权值为 $a_x+a_y$,求所有满足条件的路径中,所有点的权值和的最小值。 输入格式 第一行一个整数 $n$。 接下来 $n$ 行,每行 $n$ 个整数,表示棋盘上每个点的数字。 输出格式 输出一个整数,表示所有满足条件的路径中,所有点的权值和的最小值。 数据范围 $1\leq n\leq 300$ 输入样例 3 1 2 3 4 5 6 7 8 9 输出样例 25 算法1 (形dp) $O(n^3)$ 我们可以先将所有点的权值求出来,然后将其看作是一个有权值的图,问题就转化为了在这个图中求从 $(1,1)$ 到 $(n,n)$ 的所有路径中,所有点的权值和的最小值。 我们可以使用形dp来解决这个问题,具体来说,我们可以将这个图看作是一棵,每个点的父节点是它的前驱或者后继,然后我们从根节点开始,依次向下遍历,对于每个节点,我们可以考虑它的两个儿子,如果它的两个儿子都被遍历过了,那么我们就可以计算出从它的左儿子到它的右儿子的路径中,所有点的权值和的最小值,然后再将这个值加上当前节点的权值,就可以得到从根节点到当前节点的路径中,所有点的权值和的最小值。 时间复杂度 形dp的时间复杂度是 $O(n^3)$。 C++ 代码 算法2 (动态规划) $O(n^3)$ 我们可以使用动态规划来解决这个问题,具体来说,我们可以定义 $f(i,j,s)$ 表示从 $(1,1)$ 到 $(i,j)$ 的所有路径中,所有点的权值和为 $s$ 的最小值,那么我们就可以得到如下的状态转移方程: $$ f(i,j,s)=\min\{f(i-1,j,s-a_{i,j}),f(i,j-1,s-a_{i,j})\} $$ 其中 $a_{i,j}$ 表示点 $(i,j)$ 的权值。 时间复杂度 动态规划的时间复杂度是 $O(n^3)$。 C++ 代码

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值