[SDOI2018]战略游戏

题目描述

省选临近,放飞自我的小Q无心刷题,于是怂恿小C和他一起颓废,玩起了一款战略游戏。

这款战略游戏的地图由n个城市以及m条连接这些城市的双向道路构成,并且从任意一个城市出发总能沿着道路走到

任意其他城市。现在小C已经占领了其中至少两个城市,小Q可以摧毁一个小C没占领的城市,同时摧毁所有连接这

个城市的道路。只要在摧毁这个城市之后能够找到某两个小C占领的城市u和v,使得从u出发沿着道路无论如何都不

能走到v,那么小Q就能赢下这一局游戏。

小Q和小C一共进行了q局游戏,每一局游戏会给出小C占领的城市集合S

你需要帮小Q数出有多少个城市在他摧毁之后能够让他赢下这一局游戏。

题解

如果这是一棵树,那么做法就是直接对询问点见虚树,统计包含树中的点的最小联通块的大小。

现在它是一张无向图,那么就把圆方树建出来,考虑一条路径经过了圆->方->圆点,那么那两个圆点割点,应对答案有1的贡献,方点的贡献为0。

代码

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<vector>
#include<algorithm>
#define N 200009
using namespace std;
typedef long long ll;
int dfn[N],head[N],tot,dis[N],n,low[N],ans,num,st[N],top,rbs[N],a[N];
bool vis[N];
inline ll rd(){
    ll x=0;char c=getchar();bool f=0;
    while(!isdigit(c)){if(c==‘-‘)f=1;c=getchar();}
    while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=getchar();}
    return f?-x:x;
}
struct edge{int n,to;}e[N<<1];
inline void add(int u,int v){e[++tot].n=head[u];e[tot].to=v;head[u]=tot;}
vector<int>vec[N],ed[N];
inline bool cmp(int a,int b){return dfn[a]<dfn[b];}
struct LCA{
int top[N],size[N],son[N],fa[N],deep[N];
void clear(){memset(deep,0,sizeof(deep));memset(top,0,sizeof(top));memset(son,0,sizeof(son));memset(fa,0,sizeof(fa));}
void dfs(int u){
    size[u]=1;dis[u]=dis[fa[u]]+(u<=n);
    for(int i=0;i<vec[u].size();++i){
        int v=vec[u][i];
        fa[v]=u;deep[v]=deep[u]+1;
        dfs(v);size[u]+=size[v];
        if(size[v]>size[son[u]])son[u]=v;
    }
}
void dfs2(int u){
    dfn[u]=++dfn[0];if(!top[u])top[u]=u;
    if(son[u])top[son[u]]=top[u],dfs2(son[u]);
    for(int i=0;i<vec[u].size();++i){
      int v=vec[u][i];
      if(v!=fa[u]&&v!=son[u])dfs2(v);
    }
}
inline int getlca(int u,int v){
    while(top[u]!=top[v]){
        if(deep[top[u]]<deep[top[v]])swap(u,v);
        u=fa[top[u]];
    }
    return deep[u]<deep[v]?u:v;
}
}lca;
void tarjan(int u,int fa){
    dfn[u]=low[u]=++dfn[0];vis[u]=1;st[++top]=u;
    for(int i=head[u];i;i=e[i].n)if((i^fa)!=1){
        int v=e[i].to;if(dfn[v]>dfn[u])continue;
        if(!dfn[v]){
            tarjan(v,i);
            low[u]=min(low[u],low[v]);
        }
        else if(vis[v])low[u]=min(low[u],dfn[v]);
        if(dfn[u]==low[v]){
            ++num;vec[u].push_back(num);//cout<<u<<" "<<num<<endl;
            while(st[top]!=v){
                vec[num].push_back(st[top]);//cout<<num<<" "<<st[top]<<endl;
                vis[st[top]]=0;top--;
            }
            vec[num].push_back(v);vis[v]=0;top--;//cout<<num<<" "<<v<<endl;
        }
        if(low[v]>dfn[u]){
            vec[u].push_back(v);
            top--;vis[v]=0;
        }
    }
}
void dfs(int u){
    for(int i=0;i<ed[u].size();++i){
        int v=ed[u][i];
        dfs(v);
        ans+=dis[v]-dis[u];
        if(vis[v]&&v<=n)ans--;
    }
}
inline void init(){
    tot=1;top=0;
    memset(head,0,sizeof(head));
    memset(dfn,0,sizeof(dfn));
    memset(dis,0,sizeof(dis));
    lca.clear();
}
inline void solve(){
    n=rd();int m=rd();int u,v;
    init();
    for(int i=1;i<=m;++i){
        u=rd();v=rd();
        add(u,v);add(v,u);
    }
    num=n;
    tarjan(1,0);
    memset(dfn,0,sizeof(dfn));
    top=0;
    lca.dfs(1);lca.dfs2(1);
    memset(vis,0,sizeof(vis));
    int q=rd();
    while(q--){
        int s=rd();ans=0;
        for(int i=1;i<=s;++i)a[i]=rd(),vis[a[i]]=1;
        sort(a+1,a+s+1,cmp);
        st[top=1]=rbs[rbs[0]=1]=a[1];int root=a[1];
        for(int i=2;i<=s;++i){
            int x=a[i];//cout<<x<<"guiu"<<endl;
            int l=lca.getlca(x,st[top]);
            if(l==st[top]){st[++top]=x;rbs[++rbs[0]]=x;continue;}
            while(top>1){
                int xx=st[top],yy=st[top-1];
                if(dfn[yy]<=dfn[l]){
                    ed[l].push_back(xx);//cout<<l<<" "<<xx<<endl;
                    top--;break;
                }
                ed[yy].push_back(xx);top--;//cout<<yy<<" "<<xx<<endl;
            }
            if(dfn[l]<dfn[st[top]]){
                if(dfn[l]<dfn[root])root=l;
            //    cout<<l<<" "<<st[top]<<endl;
                ed[l].push_back(st[top]);top--;
            }
            if(l!=st[top])st[++top]=l,rbs[++rbs[0]]=l;
            st[++top]=x;rbs[++rbs[0]]=x;
        }
        while(top>1){
        //    cout<<st[top-1]<<" "<<st[top]<<endl;
            ed[st[top-1]].push_back(st[top]);
            top--;
        }
        if(!vis[root]&&root<=n)ans++;
        dfs(root);
        printf("%d\n",ans);
        while(rbs[0]){
            int x=rbs[rbs[0]];
            vis[x]=0;ed[x].clear();rbs[0]--;
        }
    }
    for(int i=1;i<=num;++i)vec[i].clear();
}
int main(){
    int T=rd();
    while(T--)solve();
    return 0;
} 

这里的圆方树不能随便的建,只能对每个点双开一个。、

具体建法为:对于非点双内的边,直接连,并且弹栈。

    if(low[v]>dfn[u]){
            vec[u].push_back(v);
            top--;vis[v]=0;
        }

对于每个割顶,新开一个节点,然后边弹栈边连边。

if(dfn[u]==low[v]){
            ++num;vec[u].push_back(num);
            while(st[top]!=v){
                vec[num].push_back(st[top]);
                vis[st[top]]=0;top--;
            }
            vec[num].push_back(v);vis[v]=0;top--;
        }

而且我们对于每个点双都只能算一次,为了避免算重,我们需要特判一下。

if(dfn[v]>dfn[u])continue

原文地址:https://www.cnblogs.com/ZH-comld/p/10384724.html

时间: 2024-10-03 05:09:08

[SDOI2018]战略游戏的相关文章

bzoj 5329 [SDOI2018] 战略游戏

bzoj 5329 [SDOI2018] 战略游戏 Link Solution 很容易想到虚树 然后发现是一个图... 现学圆方树,套上去,做完了(模板题?) 就是直接上广义圆方树先把这玩意转换成一棵树,然后对当前询问建立虚树,断掉虚树里任何一个点都合法(包括不出现的点,指那些在某个点和其虚树上父亲之间的点),统计一下即可 Code // Copyright lzt #include<stdio.h> #include<cstring> #include<cstdlib>

[SDOI2018]战略游戏 圆方树,树链剖分

[SDOI2018]战略游戏 这题是道路相遇(题解)的升级版,询问的两个点变成了\(S\)个点. LG传送门 还是先建出圆方树,考虑对于询问的\(S\)个点,答案就是圆方树上能包含这些点的最小连通块中的圆点个数减去\(S\).问题变成了怎样求这样的连通块中的圆点个数,直接给结论吧:先搞出树的dfs序,把询问的点按dfs序从小到大排一遍序,每次把答案加上第\(i\)和第\(i + 1\)个点之间的圆点个数,但是不算lca,再加上第\(1\)个和第\(S\)个点之间的圆点个数,然后除以二就得到了这个

[bzoj5329][Sdoi2018]战略游戏

题目大意:多组数据,每组数据给一张图,多组询问,每个询问给一个点集,要求删除一个点,使得至少点集中的两个点互不连通,输出方案数 题解:圆方树,发现使得两个点不连通的方案数就是它们路径上的原点个数.如何处理重复?可以按圆方树的$dfn$序排序,相邻两点求一下贡献,这样贡献就被重复计算了两次,除去$k$个询问点就行了.还有每次计算中$lca$没有被统计,发现排序后第一个点和最后一个点的$lca$一定是深度最浅的,所以只有这个点没有被统计答案,加上即可 卡点:1.圆方树$dfn$数组没赋值 2.$LC

Facebook开源游戏平台ELF: 一个用于实时战略游戏研究的轻量级平台

ELF是一个用于游戏研究的应用广泛的(Extensive).轻量级的(Lightweight).灵活的(Flexible)平台,特别适用于实时战略(RTS)游戏.在C++方面,ELF采用C++线程来并发运行多个游戏.在Python方面,ELF可以一次性返回一批游戏状态,使其对现代RL(强化学习)非常友好.另一方面,在其他平台(例如OpenAI Gym)中,一个Python接口只能包含一个游戏实例.这使得游戏的并发运行有点复杂,而这又是许多现代强化学习算法的要求. 对于RTS游戏的研究,ELF配备

战略游戏

题意/Description: Bob喜欢玩电脑游戏,特别是战略游戏.但是他经常无法找到快速玩过游戏的办法.现在他有个问题.他要建立一个古城堡,城堡中的路形成一棵树.他要在这棵树的结点上放置最少数目的士兵,使得这些士兵能了望到所有的路.注意,某个士兵在一个结点上时,与该结点相连的所有边将都可以被了望到.  请你编一程序,给定一树,帮Bob计算出他需要放置最少的士兵. 读入/Input:    输入文件中数据表示一棵树,描述如下: 第一行 N,表示树中结点的数目. 第二行至第N+1行,每行描述每个

bzoj3924 [Zjoi2015]幻想乡战略游戏 点分树,动态点分

[BZOJ3924][Zjoi2015]幻想乡战略游戏 Description 傲娇少女幽香正在玩一个非常有趣的战略类游戏,本来这个游戏的地图其实还不算太大,幽香还能管得过来,但是不知道为什么现在的网游厂商把游戏的地图越做越大,以至于幽香一眼根本看不过来,更别说和别人打仗了. 在打仗之前,幽香现在面临一个非常基本的管理问题需要解决. 整个地图是一个树结构,一共有n块空地,这些空地被n-1条带权边连接起来,使得每两个点之间有一条唯一的路径将它们连接起来.在游戏中,幽香可能在空地上增加或者减少一些军

【BZOJ3924】幻想乡战略游戏(动态点分治)

[BZOJ3924]幻想乡战略游戏(动态点分治) 题面 权限题...(穷死我了) 洛谷 题解 考虑不修改 发现一个贪心的做法 假设当前放在当前位置 如果它有一个子树的兵的总数大于总数的一半 那么,放到那个子树的根节点上一定最优 那么,现在是动态修改 考虑动态点分治 在每个点上维护子树的兵的总数 子树到上一层父亲节点 向上走产生的贡献的总和 以及接收到子节点的贡献的总和 那么,就可以计算当前点产生的贡献 于是,从分治树根开始向下贪心即可 #include<iostream> #include&l

『战略游戏 最大利润 树形DP』

通过两道简单的例题,我们来重新认识树形DP. 战略游戏(luoguP1026) Description Bob喜欢玩电脑游戏,特别是战略游戏.但是他经常无法找到快速玩过游戏的办法.现在他有个问题.他要建立一个古城堡,城堡中的路形成一棵树.他要在这棵树的结点上放置最少数目的士兵,使得这些士兵能了望到所有的路.注意,某个士兵在一个结点上时,与该结点相连的所有边将都可以被了望到. 请你编一程序,给定一树,帮Bob计算出他需要放置最少的士兵. Input Format 输入文件中数据表示一棵树,描述如下

luoguP2016 战略游戏

题目描述 Bob喜欢玩电脑游戏,特别是战略游戏.但是他经常无法找到快速玩过游戏的办法.现在他有个问题.他要建立一个古城堡,城堡中的路形成一棵树.他要在这棵树的结点上放置最少数目的士兵,使得这些士兵能了望到所有的路.注意,某个士兵在一个结点上时,与该结点相连的所有边将都可以被了望到. 请你编一程序,给定一树,帮Bob计算出他需要放置最少的士兵. 输入格式 输入文件中数据表示一棵树,描述如下: 第一行 N,表示树中结点的数目. 第二行至第N+1行,每行描述每个结点信息,依次为:该结点标号i,k(后面