zoukankan      html  css  js  c++  java
  • G. Gangsters in Central City

    给出一棵$1$为根节点的含$n$个节点的树,叶子节点都是房屋,在一个集合里面添加房屋和移除房屋。

    每一次添加和移除后,回答下面两个问题。

    1.  使得已选房屋都不能从根节点到达,最少需要砍多少条边。

    2.  在第$1$问最少砍去边的条件下,如何砍边使得从节点点开始走不能到达的非已选房屋数目最小,输出最小值。

    对于100%的数据 , $2 ≤ n ≤ 10^5 , 1 ≤ q ≤ 10^5$

    Solution :  

      首先观察到,第一问的答案。

      非常容易证明的一个上边界是砍去所有合法的$u$和1的连边,其中合法的$u$表示既和$1$直接相连,且含有有已选被选叶子节点的$1$的直接儿子$u$

      显然,这样构造是可以切断所有的已选房屋的。

      如果不那么取,在一棵有被选叶子的子树中,如果不像上面一样构造,那么为了切断已选叶子,切边必须下移,显然,为了保证该子树已选叶子被截断,那么断边的数目必须递增。

      所有对于第一问,贪心策略成立。

      在第一问思考的基础上,我们进一步想,如果找到一个临界边,若砍掉其下面的边使得被选叶子节点被切断这些边数大于1了,而砍掉上面的那条边却恰好可以切断被选叶子节点,那么问题就解决了。

      这个临界边是这个子树里面被选叶子节点集合的LCA与父亲那一条连边。

      这就是第二问的贪心策略。

      如何求一个点集在树上的LCA(深度最大的公共祖先)呢?

      结论是:求出这个点集dfs序最小的一个点$u$,dfs序最大的一个点$v$,则$lca(u,v)$即为所求。

      这是因为dfs序覆盖的范围是一个连续的区间。

      假设有三个点,DFS序分别为a,p,b且a<p<b。那么我们断言LCA(a,p,b)=LCA(a,b)。

      这是因为:两个点的LCA,就是最近的同时包含它们的那个区间所代表的点。既然LCA(a,b)的区间包含了a和b,那也一定包含了位于a和b之间的p。

      因此,对于每任意三个点,只需要保存DFS序最大和最小的点。这个点集的LCA,也就是整个点集DFS序最小的和DFS序最大的这两个点的LCA。

    至此,本题的理论部分就结束了,接下去就是实现的问题了。

    首先对于每个叶子求出它对应属于根节点的那个子树zs[u],对于每个节点求出其size[]表示含有叶子节点的数目。

    ret表示当前被标记的叶子节点数目,ans1表示第一问的答案,ans2表示被上述贪心策略覆盖总点数。

    显然,最后的答案是ans1, ans2 - ret;

    对于每个根节点子树开个set,维护当中被标记节点的dfs序,每次只需要求最大和最小即可。

    对于每个+和-分别维护就行了(动态更新ans1,ans2)。

    复杂度是$O(n log_2 n)$ 

    # include <bits/stdc++.h>
    using namespace std;
    const int N=1e5+10;
    struct rec{ int pre,to; }a[N<<1];
    int n,Q,dfn[N],tot,head[N],g[N][25],dep[N],size[N],zs[N],cnt[N];
    struct node{ int x;};
    bool operator < (node a,node b){return dfn[a.x]<dfn[b.x];}
    set<node>st[N];
    char s[10];
    void adde(int u,int v)
    {
        a[++tot].pre=head[u];
        a[tot].to=v;
        head[u]=tot;
    }
    void dfs1(int u,int fa)
    {
        dfn[u]=++dfn[0]; g[u][0]=fa,dep[u]=dep[fa]+1; size[u]=0;
        bool leaf=1;
        for (int i=head[u];i;i=a[i].pre){
            int v=a[i].to; if (v==fa) continue;
            dfs1(v,u); leaf=0; size[u]+=size[v];
        }
        if (leaf) size[u]=1;
    }
    void dfs2(int u,int fa,int rt)
    {
        bool leaf=1;
        for (int i=head[u];i;i=a[i].pre) {
            int v=a[i].to; if (v==fa) continue;
            dfs2(v,u,rt); leaf=0;
        }
        if (leaf) zs[u]=rt;
    }
    void init() {
        for (int i=1;i<=21;i++)
         for (int j=1;j<=n;j++)
          g[j][i]=g[g[j][i-1]][i-1];
    }
    int lca(int u,int v)
    {
        if (dep[u]<dep[v]) swap(u,v);
        for (int i=21;i>=0;i--)
         if (dep[g[u][i]]>=dep[v]) u=g[u][i];
        if (u==v) return u;
        for (int i=21;i>=0;i--)
         if (g[u][i]!=g[v][i]) u=g[u][i],v=g[v][i];
        return g[u][0];
    }
    int main()
    {
        freopen("gangsters.in","r",stdin);
        freopen("gangsters.out","w",stdout); 
        scanf("%d%d",&n,&Q);
        for (int i=2;i<=n;i++) {
            int u; scanf("%d",&u);
            adde(i,u); adde(u,i);
        }
        dfs1(1,0);init();
        for (int i=head[1];i;i=a[i].pre) dfs2(a[i].to,1,a[i].to);
        int ans1=0,ans2=0,ret=0;
        while (Q--) {
            int d; scanf("%s%d",s,&d);    
            if (s[0]=='+') {
                ret++;
                if (cnt[zs[d]]==0) ans1++;  cnt[zs[d]]++;
                int pre=0;
                if (st[zs[d]].size()!=0) {
                    int u=(*(st[zs[d]].begin())).x,v=(*(--st[zs[d]].end())).x;
                    pre=size[lca(u,v)];
                }
                st[zs[d]].insert((node){d});
                int u=(*(st[zs[d]].begin())).x,v=(*(--st[zs[d]].end())).x;
                int now=size[lca(u,v)];
                ans2+=now-pre;   printf("%d %d
    ",ans1,ans2-ret);
            } else {
                ret--;
                if (cnt[zs[d]]==1) ans1--;  cnt[zs[d]]--;
                int u=(*(st[zs[d]].begin())).x,v=(*(--st[zs[d]].end())).x;
                int pre=size[lca(u,v)];
                st[zs[d]].erase((node){d});
                int now=0;
                if (st[zs[d]].size()!=0) {
                    u=(*(st[zs[d]].begin())).x,v=(*(--st[zs[d]].end())).x;
                    now=size[lca(u,v)];
                }
                ans2+=now-pre;  printf("%d %d
    ",ans1,ans2-ret);
            }
        }
        return 0;
    }

     

  • 相关阅读:
    POJ 3187 Backward Digit Sums
    POJ 3669 Meteor Shower BFS
    POJ 1979 Red and Black DFS
    不想切题、
    企业管理信息系统成功实施的四个阶段
    linux下memcached的安装
    Apache开启伪静态示例
    网页设计基础知识
    memcached安装说明
    伪静态.htaccess的练习
  • 原文地址:https://www.cnblogs.com/ljc20020730/p/11260473.html
Copyright © 2011-2022 走看看