1. 程式人生 > >洛谷4606 SDOI2018戰略遊戲(圓方樹+虛樹)

洛谷4606 SDOI2018戰略遊戲(圓方樹+虛樹)

QWQ深受其害

當時在現場是真的絕望......

現在再重新來看這個題
QWQ
根據題目所說,我們可以發現,對於每一個集合中的節點,我們實際上就是要求兩兩路徑上的割點的數目

考慮到又是關於點雙的題目,而且在圖上,我們並沒有很好的辦法去做。

這時候就要考慮建出來圓方樹,然後我們對於圓方樹 的每個點,維護他到根的路徑上的圓點個數

那麼,我們該怎麼求兩兩路徑的割點總數呢(一看到資料範圍,就想到虛樹了啊)

冷靜分析一下,發現真的直接把虛樹中的點弄出來就是合法的,因為兩兩的路徑一定會通過\(lca\),而建出來虛樹,正好只會保留有用的邊。

那麼我們對於虛樹上的兩個相連的點,他們的邊權的就是兩個點到根的圓點個數的差。

這裡有一個關於虛樹的

奇技淫巧

為了忽略\(1\)號節點對答案的影響,我們可以直接選擇把所有關鍵點的\(lca\)放到虛樹的棧裡面當第一個元素,而不是1

最後對於一次詢問,我們只需要求虛樹的邊權和即可(還需要特判根的問題)

// luogu-judger-enable-o2
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<queue>
#include<map>
#include<set>
#define mk makr_pair
#define ll long long
using namespace std;
inline int read()
{
  int x=0,f=1;char ch=getchar();
  while (!isdigit(ch)) {if (ch=='-') f=-1;ch=getchar();}
  while (isdigit(ch)) {x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}
  return x*f;
}
const int maxn = 4e5+1e2;
const int maxm = 2*maxn;
int point[maxn],nxt[maxm],to[maxm];
int point1[maxn],nxt1[maxm],to1[maxm];
int cnt,cnt1;
int n,m;
int dfn[maxn],deep[maxn],low[maxn];
int f[maxn][20];
int st[maxn],tot,top;
int num;
int a[maxn],k;
int dnf[maxn],size[maxn];
void addedge1(int x,int y)
{
    nxt1[++cnt1]=point1[x];
    to1[cnt1]=y;
    point1[x]=cnt1;
}
void addedge(int x,int y)
{
    nxt[++cnt]=point[x];
    to[cnt]=y;
    point[x]=cnt;
}
void tarjan(int x,int fa)
{
    dfn[x]=low[x]=++tot;
    st[++top]=x;
    for (int i=point1[x];i;i=nxt1[i])
    {
       int p=to1[i];
       if (p==fa) continue;
       if (!dfn[p])
       {
          tarjan(p,x);
          low[x]=min(low[x],low[p]);
          if (low[p]>=dfn[x])
          {
           num++;
           addedge(num,x);
           addedge(x,num);
           do{
             addedge(num,st[top]);
              addedge(st[top],num);
              top--; 
            }while (st[top+1]!=p); 
          }
       }
       else low[x]=min(low[x],dfn[p]);
    }
}
int tmp;
void dfs(int x,int fa,int dep)
{
    int now=0;
    if (x<=n) now++;
    deep[x]=dep;
    dnf[x]=++tmp;
    size[x]=size[fa]+now;
    for (int i=point[x];i;i=nxt[i])
    {
     int p = to[i];
     if(p==fa) continue;
     f[p][0]=x;
     dfs(p,x,dep+1);
    }
}
void init()
{
    for (int j=1;j<=19;j++)
      for (int i=1;i<=num;i++)
      {
        f[i][j]=f[f[i][j-1]][j-1];
     } 
}
int go_up(int x,int d)
{
    for (int i=0;i<=19;i++)
    {
        if ((1<<i) & d) x=f[x][i];
    }
    return x;
} 
int lca(int x,int y)
{
    if(deep[x]>deep[y]) x=go_up(x,deep[x]-deep[y]);
    else y=go_up(y,deep[y]-deep[x]);
    if (x==y) return x;
    for (int i=19;i>=0;i--)
    {
        if (f[x][i]!=f[y][i])
        {
            x=f[x][i];
            y=f[y][i];
        }
    }
    return f[x][0];
}
bool cmp(int a,int b)
{
    return dnf[a]<dnf[b];
}
struct xvtree{
    int point[maxn],nxt[maxm],to[maxm];
    int val[maxm];
    int cnt;
    int s[maxn],top;
    int sum;
    void init()
    {
        cnt=0;
        sum=0;
    }
    void addedge(int x,int y,int w)
    {
     //cout<<x<<" ** "<<y<<" "<<w<<endl;
        nxt[++cnt]=point[x];
        to[cnt]=y;
        val[cnt]=w;
        point[x]=cnt; 
    }
    void dfs(int x,int fa)
    {
        for (int &i=point[x];i;i=nxt[i])
        {
            int p = to[i];
            if (p==fa) continue;
            sum+=val[i];
            dfs(p,x);
        }
    }
    int solve()
    {
        init();
        top=1;
        sort(a+1,a+1+k,cmp);
        int root=a[1];
        for (int i=2;i<=k;i++) root=lca(root,a[i]);
        s[top]=root;
        for (int i=1;i<=k;i++)
        {
         int l = lca(s[top],a[i]);
         if (l!=s[top])
         {
          while (top>1)
          {
           if (dnf[s[top-1]]>dnf[l])
           {
            addedge(s[top-1],s[top],size[s[top]]-size[s[top-1]]);//size表示他在圓方樹上和根的路徑上的圓點個數 
            top--;
                    }
                    else
                    {
                        if (dnf[s[top-1]]==dnf[l])
            {
             addedge(s[top-1],s[top],size[s[top]]-size[s[top-1]]);
             top--;
             break;
                        }
                        else
                        {
             addedge(l,s[top],size[s[top]]-size[l]);
             s[top]=l; 
             break;
                        }
                    }
                }
            }
            if (s[top]!=a[i]) s[++top]=a[i];
        }
        while (top>1)
        {
            addedge(s[top-1],s[top],size[s[top]]-size[s[top-1]]);
            top--;
        }     
        dfs(root,0);     
        if(root<=n) sum++;     
        return sum;
    }
};
xvtree xv; 
int t;
int main()
{
  t=read();
  while (t--)
  {
    tmp=0;tot=0;top=0;
    cnt=0;cnt1=0;
    xv.init();
    memset(point,0,sizeof(point));
    memset(point1,0,sizeof(point1));
    memset(f,0,sizeof(f));
    memset(dfn,0,sizeof(dfn));
    memset(low,0,sizeof(low));
    memset(dnf,0,sizeof(dnf));
    n=read(),m=read();
    num=n;
    for (int i=1;i<=m;i++)
    {
     int x=read(),y=read();
     addedge1(x,y);
     addedge1(y,x); 
     }
     for (int i=1;i<=n;i++)
     {
      if (!dfn[i]) tarjan(i,0);
     }
     dfs(1,0,1);
     init();
     int q=read();
     for (int i=1;i<=q;i++)
     {
      k=read();
      for (int j=1;j<=k;j++) a[j]=read();
         xv.init();
        cout<<xv.solve()-k<<"\n";
     }
  }
  return 0;
}