1. 程式人生 > >洛谷3348 大森林 (LCT + 虛點 + 樹上差分)

洛谷3348 大森林 (LCT + 虛點 + 樹上差分)

題目連結

這可真是道神仙題QWQ問了好多 d a l a o dalao 才稍微明白了一丟丟做法

首先,我們假設不存在 1

1 操作,那麼對於詢問的一段區間中的所有的樹,他們的形態應該是一樣的

甚至可以直接理解為 0 0 操作就是表示所有樹的生成節點都新增一個兒子

其實就算存在 1 1

操作,也是類似同理的.
這樣考慮:
考慮如果在 l l 處更換了生長節點,那麼就相當於把第 l 1 l−1
棵樹之後生長的節點都“嫁接”在這個新的生長節點上。我們可以想象對於每一個 1 1 操作建一個虛點,然後0操作生長的點都連載這個點上。然後在 l l 處 link 過去(就是說link到這個操作對應的那個節點(實點)),在 r + 1 r+1 r 處 link 回來。

相當於對於加的兒子,我們建的是實點

然後對於每一個 1 1 操作呢,我們新建一個虛點,依次掛在一號節點下面,構成一個虛鏈,我們通過把0操作的節點掛在虛點,然後從虛點連線實點,從而體現改變生長節點這個操作。

那麼QWQ對於一段區間,我們該什麼時候link,什麼時候cut呢。

這時候!離線!!

因為要求距離,那麼我們不妨把實點的點權弄成,然後虛點是0(因為虛點並沒有實際意義)

可以發現詢問與時間沒有關係。一開始我們把虛點都連成一條“虛鏈”,我們預處理出時間上離每個 0 操作最近的 1 操作是什麼,然後在這個把這個 0 操作新建的點 link 到這個虛點上。
(之所以可以這麼 l i n k link 的原因是,虛點的點權都是0,不論當前是對應的哪個生長節點,都不會產生影響,就算是1,虛鏈的總權值也是1,所以直接上去也沒錯)

這樣,剩下的操作就是 1 1 2 2

很顯然,對於每一棵樹,他們之間都是獨立的,那我們就可以把剩下的操作按照詢問端點排序
(其中,對於一個 1 操作,我們在 l l 處把它的虛點和它的父親 c u t cut 掉,然後 l i n k link 到它對應的實點下面,然後在 r + 1 r+1 c u t cut 掉它的父親, l i n k link 回鏈上)

然後依次去做,不過需要注意的是,對於一個端點來說,你需要把所有該點的修改都弄好,再去回答查詢操作)

對於查詢操作的話

這裡沒有必要 m a k e r o o t makeroot 的原因是1.有根樹2.最好是為了保持相對的父子關係不變

其實 m a k e r o o t makeroot也 可以,因為不存在子樹資訊的查詢

但是我寫的版本就是沒有 m a k e r o o t makeroot

可以直接 a c c e s s ( x ) , s p l a y ( x ) access(x),splay(x) ,那麼 s u m [ x ] sum[x] 就表示 1   x 1~x 的路徑長度,我們可以用類似查分的方式來求,也就是 s u m [ x ] + s u m [ y ] 2 s u m [ l c a ( x , y ) ] sum[x]+sum[y]-2*sum[lca(x,y)] 這裡 s u m sum 表示路徑長度

l c t lct 怎麼求 l c a lca 呢?

可以發現,我們第一次 a c c e s s ( x ) access(x) ,從根到x的路徑都是實鏈了,那麼我們再一次 a c c e s s ( y ) access(y) 的時候,最後一次輕重鏈切換的那個節點,就是 l c a lca

可以理解為兩條路徑的最深的交點

QWQ那麼到這裡,這個題基本是解決了

真的是很神仙很神仙QWQ

超級難理解啊

放上我醜陋的程式碼

// luogu-judger-enable-o2
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<queue>
#include<map>
#include<set>
#define mk makr_pair
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 = 3e5+1e2;
int ch[maxn][3];
int fa[maxn],sum[maxn],val[maxn];
int l[maxn],r[maxn]; //表示i這個實點對應的區間是哪些
int ymh[maxn]; //實點的編號 
int st[maxn];
int cnt;
int tot;
int xvgen; //最近一次1操作新建的虛點的編號 
int n,m;
int son(int x)
{
    if (ch[fa[x]][0]==x) return 0;
    else return 1;
}
bool notroot(int x)
{
    return ch[fa[x]][0]==x || ch[fa[x]][1]==x;
}
void update(int x)
{
    sum[x]=sum[ch[x][0]]+sum[ch[x][1]]+val[x]; 
}
void rotate(int x)
{
    int y=fa[x],z=fa[y];
    int b=son(x),c=son(y);
    if (notroot(y)) ch[z][c]=x;
    fa[x]=z;
    ch[y][b]=ch[x][!b];
    fa[ch[x][!b]]=y;
    ch[x][!b]=y;
    fa[y]=x;
    update(y);
    update(x);
}
void splay(int x)
{
    while (notroot(x))
    {   
        int oo=0;
        int y=fa[x],z=fa[y];
        int b=son(x),c=son(y);
        if (notroot(y))
        {
         if (b==c) rotate(y);
         else rotate(x); 
        }
        rotate(x);  
    }
    update(x); 
}
int access(int x)
{
    int y=0;
    for (;x;y=x,x=fa[x])
    {
        splay(x);
        ch[x][1]=y;
        update(x);
    }
    return y;
}
void link(int x,int y)
{
    access(x);
    splay(x);
    fa[x]=y;
}
void cut(int x)
{
    access(x);
    splay(x);
    fa[ch[x][0]]=0;
    ch[x][0]=0;
    update(x);
}
struct Node{
    int pos,opt,x,y;
};
Node a[maxn];
bool cmp(Node a,Node b)
{
    if (a.pos==b.pos) return a.opt<b.opt;
    return a.pos<b.pos;
}
int tmp[maxn];
int main()
{
  n=read();m=read();
  l[1]=val[1]=sum[1]=ymh[1]=1;
  r[1]=n;
  tot=2;
  xvgen=2;
  int real=1;
  link(tot,1);
  int oo=0;
  for (int i=1;i<=m;i++)
  {
    int opt=read();
    if(opt==0)
    {
     ymh[++real]=++tot;
     link(tot,xvgen); //每次將當前的新加入的節點,連向最近一次1修改的那個那個虛點
     int ll = read(),rr=read();
     l[real]=ll;
     r[real]=rr;
     val[tot]=sum[tot]=1;
     }
     if (opt==1)
     {
      int ll=read(),rr=read();
      int x=read();
      ll=max(ll,l[x]);
      rr=min(rr,r[x]); //看一眼這個區間是否存在 
      if (ll>rr) continue;
      ++tot;
      link(tot,xvgen); //為了構成一個類似毛毛蟲的虛鏈 
      a[++cnt]=(Node){ll,-1010,tot,ymh[x]}; //在l處將鏈斷開,然後連到這個虛點對應的實點 
        a[++cnt]=(Node){rr+1,-1010,tot,xvgen}; //r+1處把鏈連回來,重新保持虛鏈 
        xvgen=tot;
     }
     if (opt==2)
     {
      int x=read(),ll=read(),rr=read();
      a[++cnt]=(Node){x,++oo,ymh[ll],ymh[rr]}; //把詢問也記錄,這裡第二位的作用是,保證了斷鏈和復原,一定在詢問之前 
     }
  }
  sort(a+1,a+1+cnt,cmp);//詢問排序 
  for (int i=1;i<=cnt;i++)
  {
    int ans=0; 
    if(a[i].opt>0)
     {
      access(a[i].x),splay(a[i].x),ans+=sum[a[i].x];
      int l = access(a[i].y);
         splay(a[i].y),ans+=sum[a[i].y];
         access(l),splay
            
           

相關推薦

3348 森林 (LCT + + 樹上)

題目連結 這可真是道神仙題QWQ問了好多 d a l

2018.11.09【NOIP2016】【P1600】天天愛跑步(樹上

傳送門 解析: 據說這是NOIP歷年最難一道題。。但是真的沒有寶藏難啊我覺得。。。 思路: 答案分兩類統計,一種是子樹中過來,一種是其他地方過來。那麼路徑就被拆分成兩部分了,一部分是S−&gt;lcaS-&gt;lcaS−>lca,一部分

luoguP3128 [USACO15DEC]最流Max Flow 題解(樹上)

gist ref dep efi != ace head turn gis 鏈接一下題目:luoguP3128 [USACO15DEC]最大流Max Flow(樹上差分板子題) 如果沒有學過樹上差分,摳這裏(其實很簡單的,真的):樹上差分總結 學了樹上差分,這道題就極其顯然

BZOJ 3331 (Tarjan縮+樹上

題面 傳送門 分析 用Tarjan求出割點,對點-雙連通分量(v-DCC)進行縮點,圖會變成一棵樹 注意v-DCC的縮點和e-DCC不同,因為一個割點可能屬於多個v-DCC 設圖中共有p個割點和t個v-DCC,我們建立一張包含p+t個點的新圖,並將每個割點和包含它的所有v-DCC連邊 縮點後原圖中一般點

[bzoj3331][BeiJing2013]壓力(tarjan雙縮+樹上

題目連結:傳送門 必須經過的點,也就是繞不開的點,就是割點(這就是割點的定義)。那麼對於所有詢問,非割點的必須經過次數,就是這個點作為詢問的開頭或者結尾的次數。 找出所有的點雙聯通分量,然後縮點(這裡注意把割點單獨作為一個點),它將是一棵樹。然後把詢問放到樹上,作樹上差分。 要注意的是新圖的節點數可能大

【USACO15DEC】最流Max Flow(樹上

聽了教練的考前須知 蒟蒻緊張的要死 只想做信心題 #include<bits/stdc++.h> #define N 50005 using namespace std; int n,k,tot,first[N]; struct Tree { int to,next; }edge[2*N

P3348 [ZJOI2016]森林LCT樹上

cst log 函數 默認 位置 想法 差分 truct AC 洛谷題目傳送門 思路分析 最簡單粗暴的想法,肯定是大力LCT,每個樹都來一遍link之類的操作啦(T飛就不說了) 考慮如何優化算法。如果沒有1操作,肯定每個樹都長一樣。有了1操作,就來仔細分析一下對不同樹的影響

P3128 [USACO15DEC]最流Max Flow-樹上(權)(模板題)

因為徐州現場賽的G是樹上差分+組合數學,但是比賽的時候沒有寫出來(自閉),背鍋。 會差分陣列但是不會樹上差分,然後就學了一下。 看了一些東西之後,對樹上差分寫一點個人的理解:   首先要知道在樹上,兩點之間只有一條路徑。樹上差分就是在樹上用差分陣列,因為是在樹上的操作,所以要用到lca,因為

—— P2387 魔法森林

書法 輸出 -m 魔法森林 include i++ queue col 這一 題目描述 為了得到書法大家的真傳,小 E 同學下定決心去拜訪住在魔法森林中的隱 士。魔法森林可以被看成一個包含 n 個節點 m 條邊的無向圖,節點標號為 1,2,3,…,n,邊標號

P3388 【模板】割(割頂)

span iostream 模板 pri add ++ 割點 logs () 表示割點模板很難理解。。。。但是呢,可以將整個圖用深搜來一步步遞歸。。 dfn[x]<=low[tmp] && x!=mr的點就++;完畢。。。。PS:小心第一個節點。。。

P3388 【模板】割(割頂)

tex def its next set clas pro != bit P3388 【模板】割點(割頂) 題目背景 割點 題目描述 給出一個n個點,m條邊的無向圖,求圖的割點。 輸入輸出格式 輸入格式: 第一行輸入n,m 下面m行每行輸入x,y表示x

P3627 [APIO2009]搶掠計劃 縮+spfa

分享 lin 通過 輸入輸出 設有 size 註意 tdi clear 題目描述 Siruseri 城中的道路都是單向的。不同的道路由路口連接。按照法律的規定, 在每個路口都設立了一個 Siruseri 銀行的 ATM 取款機。令人奇怪的是,Siruseri 的酒吧也都設在

P3387 【模板】縮

badge mes memset define http style using cst 路徑 P3387 【模板】縮點 題目背景 縮點+DP 題目描述 給定一個n個點m條邊有向圖,每個點有一個權值,求一條路徑,使路徑經過的點權值之和

模板匯總(可能敲不完了qaq)

最長公共子序列 through typedef close 原來 show tex das ora 前言   哇!突然發現搜索‘模板’能搜到一坨。。。開始了默默刷模板的漫長之路。。。就讓我最後在掙紮一下下吧!!!   待續。。。持續更新中。。。

3348

ons ID pre algo ace src 模板 bool urn 可持久化線段樹模板 1.結構體的打法 #include<cstdio> #include<cctype> #include<algorithm> using nam

P3387 【模板】縮 【Tarjan SCC】

str 新的 ace map sco pop 建圖 出棧 多個 1 #include<iostream> 2 #include<vector> 3 #include<stack> 4 #include<queue&

P3128 [ USACO15DEC ] 最流Max Flow —— 樹上

一次 code 差分 name print ref using swa www 題目:https://www.luogu.org/problemnew/show/P3128 倍增求 lca 也寫錯了活該第一次慘WA。 代碼如下: #include<iostream&

3128 [USACO15DEC]最流Max Flow——樹上

getchar ring www. tar pre ostream pan oid cst 題目:https://www.luogu.org/problemnew/show/P3128 樹上差分。用離線lca,鄰接表存好方便。 #include<iostream&g

P3950 部落衝突 LCT

Code: #include <cstdio> #include <algorithm> #include <string> #include <cstring> using namespace std; void setIO(string a)

P1262 間諜網路 ( tarjan+縮) 題解

題目來源: 題目描述: 題目描述 由於外國間諜的大量滲入,國家安全正處於高度的危機之中。如果A間諜手中掌握著關於B間諜的犯罪證據,則稱A可以揭發B。有些間諜收受賄賂,只要給他們一定數量的美元,他們就願意交出手中掌握的全部情報。所以,如果我們能夠收買一些間諜的話,我們