原題鏈結
線段樹基本板子的可塑性其實非常強悍,針對不同的題目要求只要稍作修改就可以發揮不同的作用。這道題讓我更深刻地理解了這一點。
本題和普通的樹鏈剖分+線段樹最大的區別在於它的每個結點並不儲存乙個數值,而是只有兩種狀態:安裝與未安裝。針對這一特點,我們對線段樹的板子做以下修改即可:
lazy標記:由於現在每個節點只有兩種狀態,\(lazy\)標記也只有兩種數值就夠了,比如\(1\)代表該區間所有軟體全部被安裝,\(-1\)代表該區間所有軟體全部沒有被安裝。
pushdown操作:之前\(pushdown\)僅僅把父節點的\(lazy\)標記下放即可,但現在\(lazy\)標記有兩種,所以下放之前應當先判斷:\(lazy\)標記為\(1\)則代表全部安裝,那麼就應當把子節點的安裝數量變為區間內總節點數;\(lazy\)標記為\(1\)則代表全部解除安裝,那麼就應當把子節點的安裝數量變為\(0\)。
update操作:之前傳參時傳遞的是向該區間內加的數,現在則應更改為操作模式,即解除安裝還是安裝?安裝則把\(lazy\)標記記為\(1\),把子節點的安裝數量變為區間內總節點數;解除安裝則把\(lazy\)標記記為\(-1\),把子節點的安裝數量變為\(0\)。
其餘操作均保持不變
#includeusing namespace std;
const int maxn=100000+3;
int head[maxn],tot,dep[maxn],size[maxn],son[maxn],top[maxn],n,q,fa[maxn],dfn[maxn],dfn_clock;
struct edgee[maxn<<1];
struct nodetree[maxn<<2];
void add(int a,int b)
void dfs(int u,int father)
}void get_top(int u,int t)
}void build(int rt,int l,int r)
void renew(int rt)
void push_down(int rt)
if(tree[rt].lazy==-1)
tree[rt].lazy=0;
}void update(int rt,int s,int t,int mode)
push_down(rt);
int mid=tree[rt].l+tree[rt].r>>1;
if(s<=mid) update(rt<<1,s,t,mode);
if(t>mid) update(rt<<1|1,s,t,mode);
renew(rt);
}int query(int rt,int s,int t)
int ask(int u)
ans+=query(1,1,dfn[u]);
return ans;
}void treeadd(int u,int mode)
update(1,1,dfn[u],mode);
}int main(){
scanf("%d",&n);
for(int i=1;i幸甚至哉,歌以詠志。
noi2015軟體包管理器
你決定設計你自己的軟體包管理器。不可避免的,你要解決軟體包之間的依賴關係。如果a依賴b,那麼安裝a之前需安裝b,解除安裝b之前須解除安裝a。0號軟體包不依賴任何軟體包。依賴關係不存在環 包括自環 你的任務是,求出每次安裝 刪除操作會改變多少個包的狀態。安裝乙個已安裝的軟體包,或者解除安裝乙個未安裝的...
Noi2015 軟體包管理器
time limit 10 sec memory limit 512 mb 你決定設計你自己的軟體包管理器。不可避免地,你要解決軟體包之間的依賴問題。如果軟體包a依賴軟體包b,那麼安裝軟體包a以前,必須先安裝軟體包b。同時,如果想要解除安裝軟體包b,則必須解除安裝軟體包a。現在你已經獲得了所有的軟體...
NOI2015 軟體包管理器
樹鏈剖分維護。1表示安裝的狀態,0表示沒有安裝的狀態。如果install就是把當前點到根的所有點都變成1,然後計算前後的 delta 如果uninstall呢,就是把自己的子樹變成0,答案也是前後的 delta 具體可以參照 不過我的 常數好大啊,跑得好慢。如下 include include in...