[2018.5.24集训]山景城-博弈论

题目大意

绝顶聪明的A和B在一棵树上博弈。

A的目标是进行最少次数的操作,使B到达节点t。每回合A可以进行三种决策:
1.不操作(这不算操作次数) 2.切断一条树上的边 3.消除一条边上B留下的标记。

B初始位于节点s,目标是在到达t之前使A进行尽可能多的操作。
每回合,若B所处节点存在没有被切断也没有留下标记的边,则B必须从这些边之中选择一条走过去,并在这条边上做标记,否则原地不动。

求A的最终操作次数。

部分分:s和t之间有一条边。

题解

假定令t为树根。

首先考虑部分分。
观察可知,最后B一定会走到一个死路,然后A在封锁B所在节点到t上的其他边后,沿路进行消除标记操作强行让B到达t。
并且,A在B走到死路之前进行的消除标记操作是不优的,会导致B的下一步的选择变多,即使不会改变最优答案,将来再消除和现在消除的总次数也是一样的,该切断的边也还是要切,不如优先假定消除标记操作全部在最后进行。

然后考虑一个dp:设 $f_i$ 代表B第一次来到 $i$ 节点,并被A强制送回 $i$ 节点这段时间内,A的总操作次数。

那么显然每次A会选择切掉当前点和子树中 $f$ 值最大的点相连的边,而B会选择次大值向下走。
同时,在将B引到当前节点时,应确保当前节点周围的边除了通往t的边和B到达这个位置的边之外的边全部被切断。因此有转移方程:

$$f_u = deg_u-2 + \mathrm{second?max}_{v \in child_u}{f_v}$$

最后得到的 $f_s$ 即为部分分答案!

对于原问题,令可以发现B多出了操作:向上走,而A不能切断s到t之间的边,否则B将无法到达t。
于是只能让B随意往上走,但一旦中途B选择进入某个儿子,B下一次在链上就是被A强制移动了。

首先对这条链上的每个节点进行一次dp,接下来的操作包括这次dp均不考虑这些点在s-t链上的儿子。
在dp结束后,给当前点的每个儿子的 $f$ 值加上它到t路径上所节点的儿子数量,因为要防止B再次进入另一个儿子从而带来负收益。
因此,现在每个链上节点的儿子的 $f$ 值,代表了若B选择进入当前儿子后,剩余需要完成目标的花费。

直接对链dp不太好做,那么考虑二分答案。
考虑如何判断一个答案是否合法:
从s到t依次枚举链上的节点,令s为1号节点,则在B到达 $i$ 号节点并做出下一步决策前,$A$ 可以有 $i$ 次操作机会。

假如当前二分值为 $lim$。
设 $x$ 为为了使答案合法,目前为止至少需要使用的操作次数。
对于节点 $i$ 的儿子 $v$,若 $x+f_v>lim$,则在B在 $i$ 节点上并还未做出下一步决策之前,$x$ 与 $v$ 之间的边必须切断,否则若B选择进入 $v$ ,答案将不合法。

于是,判定条件为,在扫完 $i$ 的所有儿子后,$x$ 加上 $i$ 需要切断的儿子数,若 $x>i$ 或 $x>lim$,则 $lim$ 不合法。

于是二分一发就完成了~

代码:

#include<cstdio>
#include<vector>
#include<cstdlib>
#include<algorithm>
using namespace std;

inline int read()
{
    int x=0;char ch=getchar();
    while(ch<'0' || '9'<ch)ch=getchar();
    while('0'<=ch && ch<='9')x=x*10+(ch^48),ch=getchar();
    return x;
}

const int N=1e6+9;

int n,s,t,p,las;
vector<int> g[N];
int f[N],fa[N];
int stk[N],top;

inline void dfs(int u)
{
    for(int i=0,v;i<g[u].size();i++)
        if((v=g[u][i])!=fa[u])
        {
            fa[v]=u;
            dfs(v);
        }
}

inline void dfs2(int u)
{
    if(!g[u].size())
    {
        f[u]=0;
        return;
    }
    int mx1=0,mx2=0;
    for(int i=0;i<g[u].size();i++)
    {
        dfs2(g[u][i]);
        if(f[g[u][i]]>=mx1)
            mx2=mx1,mx1=f[g[u][i]];
        else if(f[g[u][i]]>mx2)
            mx2=f[g[u][i]];
    }
    f[u]=mx2+g[u].size();
}

namespace faq
{
    inline bool check(int x)
    {
        int must=0;
        for(int i=1;i<=top;i++)
        {
            int cnt=0;
            for(int j=0;j<g[stk[i]].size();j++)
                if(f[g[stk[i]][j]]+must>x)
                    cnt++;
            must+=cnt;
            if(must>i || must>x)return 0;
        }
        return 1;
    }

    int mina()
    {
        dfs2(s);
        stk[top=1]=s;
        for(int x=fa[s],las=s;x!=t;las=x,x=fa[x])
        {
            stk[++top]=x;
            g[x].erase(find(g[x].begin(),g[x].end(),las));
            dfs2(x);
        }

        int tot=0;
        for(int i=top;i>=1;i--)
        {
            tot+=g[stk[i]].size();
            for(int j=0;j<g[stk[i]].size();j++)
                f[g[stk[i]][j]]+=tot;
        }

        int l=0,r=n,mid,ans=n;
        while(l<=r)
        {
            mid=l+r>>1;
            if(check(mid))
                ans=mid,r=mid-1;
            else
                l=mid+1;
        }

        printf("%d\n",ans);
        return 0;
    }
}

int main()
{
    n=read();t=read();s=read();
    for(int i=2,u,v;i<=n;i++)
    {
        u=read();v=read();
        g[u].push_back(v);
        g[v].push_back(u);
    }

    if(s==t)return puts("0"),0;

    dfs(t);return 0;
    for(int i=1;i<=n;i++)
        if(i!=t)g[i].erase(find(g[i].begin(),g[i].end(),fa[i]));

    return faq::mina();
}

原文地址:https://www.cnblogs.com/zltttt/p/9091238.html

时间: 2024-11-02 23:44:01

[2018.5.24集训]山景城-博弈论的相关文章

[2018湖南省队集训] 6.24 T1 marshland

题面在这里! 一开始感觉像一个类似二分图的最小割,于是成功跑偏2333333 很容易发现一个关键性质,'L'的两个角落在的偶数格 的行(或者列)的奇偶性一定不同.... 于是我们再把偶数格按照行(或者列)的奇偶性再细分成 两类,可以发现只有一个奇数格向旁边的两类偶数格都有空挡的话,才能放下一个L. 所以我们把放L看成网络中的一条流量,要经过三种点,于是对于奇数格拆点限流然后四列点直接跑最大费用最大流就行了.... 因为不用把m个L都放完,所以增广到 dis<0 的时候跳出就好啦.... #inc

2017冬季24集训模拟-3.耀西岛

--------------------------题解 路径的长度是1-200000 然后路径的条数有n*(n+1)/2 根据鸽巢原理n*(n+1)/2 > 200000就一定是YES 所以复杂度只有n^2 1 #include <iostream> 2 #include <queue> 3 #include <set> 4 #include <cstdio> 5 #include <cstring> 6 #include <vec

2017冬季24集训模拟-1.寻找幽灵

--------------------------------------------题解 把最短路处理出来然后做背包 没有把head数组和all初始化qwq 1 #include <iostream> 2 #include <queue> 3 #include <set> 4 #include <cstdio> 5 #include <cstring> 6 #include <vector> 7 #include <algo

2017冬季24集训模拟-4.排座椅

--------------------题解 统计这一列或行放通道能隔开几个人,然后贪心 输出没有排序QWQ 1 #include <iostream> 2 #include <queue> 3 #include <set> 4 #include <cstdio> 5 #include <cstring> 6 #include <vector> 7 #include <algorithm> 8 #define siji(i

2018.1.24 7周3次课

七周三次课(1月24日) 10.11 Linux网络相关 10.12 firewalld和netfilter 10.13 netfilter5表5链介绍 10.14 iptables语法 10.11 Linux网络相关 用ifconfig命令查看网卡IP 前面曾用过ip addr这个命令来查看系统的IP地址.其实在centos 7之前,我们使用最多的命令是ifconfig,它类似于Windows的ipconfig命令,后面不加任何选项和参数时,只打印当前网卡的相关信息 (如子网掩码. 网关等).

2018.4.24 java实现8皇后算法

import java.util.Scanner; public class Nqueens { private boolean verify(int[] arr,int i) { // TODO Auto-generated method stub for (int j = 1; j != i; j++) { if(j == i) { continue; } if(arr[i]==arr[j]||Math.abs(i-j)==Math.abs(arr[i]-arr[j])) { return

2018/10/24

两年前,也就是2016年10月24号,也是一个对于程序员来说比较好记的日子,开始在CSDN写博客,以技术类为主.通过把平时开发过程中遇到的一些问题总结记录下来,这对于深入理解问题很有帮助,经常归纳总结对于技术水平也有很大提升.另外,还可以让遇到类似问题的筒子们少走一些弯路,大家共同进步. 可是现在,CSDN已经彻底沦陷在广告的包围之中,恶心至极,我想要一个干净清爽的环境,于是选择了简书和博客园.希望以后能坚持不断更新博客,不断学习,不断进步! 原文地址:https://www.cnblogs.c

分类导航(更新时间:2018.1.24)

新的一年,将在2017下半年所写的博文作了个总结,也是方便自己以后查阅,整理完也是发现在这半年而是学到了许多,新的一年,继续努力! Android开发系列 Android开发——使用intent传递对象 Android开发——使用高级的RecyclerView实现侧滑菜单删除功能(SwipeRecyclerView) Android开发——使用LitePal开源数据库框架 Android ListView与RecycleView的对比使用 Android 自定义控件 Android百分比布局成功

2018.1.24 扇贝新闻精选(3/10)

Aled Jones to return to the BBC Aled Jones(阿雷德·琼斯)回归BBC Singer Aled Jones is to resume presenting programmes on the BBC, the corporation has announced. BBC公司宣布,歌手阿雷德·琼斯将继续出席BBC节目. ★ resume doing sth:重新开始做某事 ★ present programmes:出席节目 Aled Jones(阿雷德·琼斯