題解原發於我的blog
首先,很明顯這是一道樹鏈剖分的題。
注意到乙個軟體只會以來乙個軟體,並且不會出現環,所以每次都可以連一條\((x\ ,\ i)\)的邊。
當安裝乙個軟體時,就把\((1\ ,\ x)\)的路徑上所有的點的轉態變為\(1\)
但解除安裝乙個軟體時,就把\(x\)及它的所有的子樹變為\(0\)
線段樹維護即可
推薦一道樹鏈剖分的好題【模板】樹鏈剖分
做完這道題就可以差不多學完所有關於樹鏈剖分的芝士
最後貼上**知道你們只看這個:
#include #include using namespace std;
template inline void read(t &x)
while (s >= '0' && s <= '9')
if (f)
x = (~x) + 1;
}#define re register
#define ls (k << 1)
#define rs (k << 1 | 1)
const int n = 1e5 + 10, m = 2e5 + 10, t = 4e5 + 10;
struct edge
edge[m];
int num_edge, head[n];
struct tree
tree[t];
char s[110];
int n, q, cnt;
int idx[n], rk[n], dep[n], fa[n], top[n], size[n], son[n]; //rk[i]其實並沒有什麼用這只是我的習慣
inline void add_edge(int from, int to)
inline void dfs1(int u, int f)
}inline void dfs2(int u, int tp)
}inline void pushdown(int k)
}inline void build(int k, int l, int r)
int mid = l + r >> 1;
build(ls, l, mid);
build(rs, mid + 1, r);
}inline void update1(int k, int l, int r, int val)
int mid = (tree[k].l + tree[k].r) >> 1;
pushdown(k);
if (l <= mid)
update1(ls, l, r, val);
if (mid < r)
update1(rs, l, r, val);
tree[k].sum = tree[ls].sum + tree[rs].sum;
}inline void update2(int x, int y)
if (dep[x] > dep[y])
swap(x, y);
update1(1, idx[x], idx[y], 1);
}int main()
dfs1(1, 0);
dfs2(1, 1);
build(1, 1, n);
read(q);
for (re int i = 1, before, x; i <= q; ++i)
else if (s[0] == 'u')
}return 0;
}
馬蜂差評 洛谷 P2146 NOI2015 軟體包管理器
如果乙個軟體被解除安裝,那答案就是它所有已安裝的子孫的個數 如果安裝,就是它到根的鏈上沒安裝的個數.注意修改lazy的時候 1 include2 include3 include4 include5 6using namespace std 78 int n,head 100001 rk 10000...
P2146 NOI2015 軟體包管理器
很好的樹剖板子題 操作一 求點x到0的最短路徑 經過的結點個數 路徑上已安裝的軟體包的總個數,同時將經過的路徑上的所有點標記為已安裝。操作二 將子樹代表的那段存安裝包個數的區間清空 或者是說將其sum值賦值為0 此處為了實現標記已安裝和清空,我們用了乙個標記add add i 1 線段樹上的點i及其...
P2146 NOI2015 軟體包管理器
輸入格式 從檔案manager.in中讀入資料。輸入檔案的第1行包含1個整數n,表示軟體包的總數。軟體包從0開始編號。隨後一行包含n 1個整數,相鄰整數之間用單個空格隔開,分別表示1,2,3,n 2,n 1號軟體包依賴的軟體包的編號。接下來一行包含1個整數q,表示詢問的總數。之後q行,每行1個詢問。...