1. 程式人生 > >Codeforces 1017G The Tree(分塊DFS)

Codeforces 1017G The Tree(分塊DFS)

Codeforces 1017G The Tree

題目大意:

給一個一開始所有節點都是白色的樹,給一些查詢操作,給的三種操作:
1.在v的所有子節點中向下深搜,直到找到第一個白色子孫節點(或者自己),染成黑色。
2.把v的所有子樹(包括它自己)全部染白。
3.輸出v節點的顏色。

思路:

一般想到就是大模擬,直接暴力深搜Q次,操作3是O(1),其他都是O(n),時間複雜度總的就是。。。。O(nq),Boom~
所以看了看官方題解,畢竟這個是相當於Div1 E題難度的題目,我這種接近Newbie警告的pupil,怎麼可能會寫。。。但是看過官方題解覺得好像不難啊,就決定花一個早上研究了一下寫掉了,而且發現標程裡面有些東西是不太必要的,比如那個存放原本狀態的old_black陣列。初始化我改用了memset,比for迴圈稍好,並且不像標程一樣每一個V2陣列都要重新初始化,我只初始化本輪查詢需要用到的就可以了。然而。。。並沒有什麼卵用,總的思路和標程是一樣一樣的。
總的思路是對查詢分塊建立一棵小樹,只存放被查詢到的點,並記錄被查詢到的節點間白色塊的數量,在小樹上DFS進行三種操作,直到該塊查詢全部預處理完成後再將更新應用到整棵樹上。總的時間複雜度一下子就降到了O(

nn)級別
主要節省的時間在兩個地方:
1.分塊後,每塊查詢的時候,並不需要每次遍歷每個節點,跳過了一些無關緊要的節點最後再更新。
2.有些重複的操作,預處理的時候就能避免掉,比如對同一個節點反覆進行操作2的清空,或者一直間或染黑染白。
程式碼(帶詳細的註釋(雖然圖方(zhuang)便(bi)全都是英文)

#include <iostream>
#include <vector>
#include <cmath>
#include <cstring>
using namespace std;
struct mini//the data structure storing edges of the mini-tree
{ int to;//the son vertex int white;//the number of white vertices between two vertices we need to operate in the mini-tree int dis;//the distance between two mini-tree vertices in the original tree, we need this to do the operation 2 }; int v[100005];//the vertex we need to operate int t[100005];//the type of operation we do
vector<int> edge[100005];//the origin tree, which has no weight on its edges. So we just need to storage its end. vector<mini> minitree[100005];// A mini Tree, with a weighted edge bool vis[100005];//mark the vertex if we need to add the vertex to the mini-tree bool cl[100005];//mark if the clear operation should be done bool isblack[100005];//mark the vertex. isblack[i] is true when i-th vertex is black int pusher[100005];//check how many operation 1s should be done to the son void builder(int now,int pre,int white,int dis) { //cout<<now<<endl; if(vis[now]) { if(pre!=0) { mini temp; temp.to=now; temp.white=white; temp.dis=dis; minitree[pre].push_back(temp);//add an edge to the mini-tree } for(int i=0; i<edge[now].size(); i++) { builder(edge[now][i],now,0,0);//clear the number of whites and the distance } } else { if(!isblack[now]) { white++;//count the white vertices } for(int i=0; i<edge[now].size(); i++) { builder(edge[now][i],pre,white,dis+1);//bypass all other vertices but count the white ones between two key vertices } } } void op1(int now) { if(!isblack[now]) { isblack[now]=true;//if the vertex is still white, just paint it black and quit return; } else { pusher[now]++;//remember the blocks to be drawn but not now,save some time if clear op is found; for(int i=0; i<minitree[now].size(); i++) { if(pusher[now]>minitree[now][i].white) { minitree[now][i].white=0;//this doesn't matter, the white value won't be used any more this round (because both of the vertices are marked black,you may never go to line 62,where is the only line which need this value again.),but we know, in this situation, it's natural to think that no vertices between the father and the son are white op1(minitree[now][i].to);//if the operation 1 was done more than the white vertices between two key vertices, just do it for his son } } } } void op2(int now) { isblack[now]=false; pusher[now]=0;//all the sons are marked white, so all the pushes we made were abandoned cl[now]=true; for(int i=0; i<minitree[now].size(); i++) { minitree[now][i].white=minitree[now][i].dis;//if the vertex should be marked black again ,you may found this is affecting line 62. op2(minitree[now][i].to); } } void push_down(int now,bool clr,int push) { if(vis[now]) { clr=cl[now]; push=pusher[now]; } else//the vertices in minitree has correct status already if there's no else, you will find if the direct father of the visited vertex who have done the operation 2 already has a push number of 1, the vertex will be wrongly marked white { if(clr)//if the vertex was cleared before, we need to clear it first, because operation2's area is much bigger than the operation 1,and the vertex may be marked black again. { isblack[now]=false; } if(!isblack[now] && push>0) { isblack[now]=true; push--; } } for(int i=0; i<edge[now].size(); i++) { push_down(edge[now][i],clr,push); } } int main() { ios::sync_with_stdio(false); cin.tie(0); cout.tie(0); int n,q; cin>>n>>q; for(int i=2; i<=n; i++) { int temp; cin>>temp; edge[temp].push_back(i); } for(int i=1; i<=q; i++) { cin>>t[i]>>v[i]; } memset(isblack,0,sizeof(isblack));//all the vertices were white before we do the operations int block=sqrt(n); for(int i=1; i<=q; i+=block)//Block the queries into pieces, one loop O(n+sqrt(n)+sqrt(n)+n),totally O(2(n+n*sqrt(n))) { memset(vis,0,sizeof(vis));//memset() is a little bit faster than the O(n) for loop, but still O(n) memset(cl,0,sizeof(cl)); memset(pusher,0,sizeof(pusher)); for(int j=0; j<block && i+j<=q; j++)//O(block),or O(sqrt(n)) { minitree[v[i+j]].clear();//we just need to initialize the vectors we will use this time vis[v[i+j]]=true; } builder(1,0,0,0);//Build the mini tree with the original tree so that you can save a lot of time by bypassing useless vertices.This method is based on DFS, Traversal every vertex once, O(n) for(int j=0; j<block && i+j<=q; j++) { switch(t[i+j]) { case 1: op1(v[i+j]);//DFS once in mini-tree, no more than O(block) or O(sqrt(n)) break; case 2: op2(v[i+j]);//also DFS in mini-tree, O(block) or O(sqrt(n)) break; case 3: if(isblack[v[i+j]]) { cout<<"black\n"; } else { cout<<"white\n"; } } } push_down(1,0,0);// renew the tree,DFS in original tree,O(n) } return 0; }

相關推薦

Codeforces 1017G The TreeDFS

Codeforces 1017G The Tree 題目大意: 給一個一開始所有節點都是白色的樹,給一些查詢操作,給的三種操作: 1.在v的所有子節點中向下深搜,直到找到第一個白色子孫節點(或者自己),染成黑色。 2.把v的所有子樹(包括它自己)全部

Codeforces Round #514 (Div. 2) E. Split the Tree 貪心 + 樹上倍增

題目大意:給出一棵有 n 個結點的樹,每個結點都有一個權值 w ,現在要你將這棵樹分成若干條鏈,且每個結點只能屬於一條鏈,分出來的鏈滿足每條鏈上的結點不超過L個,同時這些結點的權值和不超過S。問你最少能把這棵樹分成幾條鏈。 題目思路:由於是要使得鏈儘可能的少,所以分出來

CodeForces - 149D Coloring Brackets 區間DP+DFS

Once Petya read a problem about a bracket sequence. He gave it much thought but didn't find a solution. Today you will face it. You are given string

hdu4417+二分

題目: 給n個數,數中有重複的。有m個詢問,問的是[L,R] 區間內有多少個數小於等於h。 分析: 快速查詢——排序(快排),二分 注意: 二分邊界 #include<bits/stdc++.h> #define maxn 100010 using namespace

luogu2801教主的魔法+二分

洛谷P2801 題目: 教主最近學會了一種神奇的魔法,能夠使人長高。於是他準備演示給XMYZ資訊組每個英雄看。於是N個英雄們又一次聚集在了一起,這次他們排成了一列,被編號為1、2、……、N。 每個人的身高一開始都是不超過1000的正整數。教主的魔法每次可以把閉區間[L, R](1≤L

Gym - 101972B Updating the Tree 深搜+合併

B. Updating the Tree time limit per test 1.5 s memory limit per test 256 MB input standard input output standard output A rooted t

1059E Split the Tree貪心+樹上倍增

E. Split the Tree time limit per test 2 seconds memory limit per test 256 megabytes input standard input output standard output Y

hdu-4836-The Query on the Tree線段樹+LCA

題目連結 思路:對於每次詢問,主要是看x和root的關係,求出root和xlca root=x ,ans為總的和 lca=x  那麼ans=總的和-(root到x這條鏈上父節點為x的那個點的子樹和) 否則,ans就是x的子樹和 求子樹和和修改直接線段樹維護。節點的編

101972B Updating the Tree 深搜+合併

B. Updating the Tree time limit per test 1.5 s memory limit per test 256 MB input standard input output standard output A rooted

poj 1426 Find The Multiple 簡單搜尋dfs

題目: Given a positive integer n, write a program to find out a nonzero multiple m of n whose decimal representation contains only the digits 0 and 1.

HTTP 斷點續傳傳輸

簡述 斷點續傳:指的是在上傳/下載時,將任務(一個檔案或壓縮包)人為的劃分為幾個部分,每一個部分採用一個執行緒進行上傳/下載,如果碰到網路故障,可以從已經上傳/下載的部分開始繼續上傳/下載未完成的部分,而沒有必要從頭開始上傳/下載。可以節省時間,提高速度。

poj 3233 矩陣乘法矩陣

題解:Sn為所求矩陣, 則 這樣, 此題就變成了求矩陣冪和矩陣乘法, 分塊矩陣乘法和普通矩陣一樣的。 code: /* adrui's submission Language : C++ Result : Accepted Love : ll Favorite

HDU 4836 —— The Query on the Tree線段樹+LCA

下午百度之星複賽裡最簡單的一題,雖然我還是1個小時才AC的,呃,下午果斷被虐粗翔。 本題磨了1個小時才過,第1題還是很猥瑣地用了隨機數過的(真不知道怎麼做)。 回到這題來,其實也不知道大牛們怎麼做的,我只能用線段樹+LCA搞了。 首先考慮根不改變的情況,那麼我們可以將每個

Avoid The Lakes 深搜dfs

Farmer John's farm was flooded in the most recent storm, a fact only aggravated by the information that his cows are deathly afraid of water. His insuranc

1057 Stack 思想

out esp col file nts return while com eve Stack is one of the most fundamental data structures, which is based on the principle of Last I

Codeforces 506D Mr. Kitayuta's Colorful Graph + 並查集

pair 重新 emp %d 操作 its -- 是否 細節問題 題目鏈接 Mr. Kitayuta‘s Colorful Graph 把每種顏色分開來考慮。 所有的顏色分為兩種:涉及的點的個數 $> \sqrt{n}$ 涉及的點的個數 $<= \s

[bzoj4765]普通計算姬+樹狀數組+DFS

-- 位置 print names ont div 修改 turn nlog 題意 給定一棵n個節點的帶權樹,節點編號為1到n,以root為根,設sum[p]表示以點p為根的這棵子樹中所有節點的權值和。計算姬支持下列兩種操作: 1 給定兩個整數u,v,修改點u的權值為v。

ACM-ICPC 2018 瀋陽賽區網路預賽 J. Ka Chang +樹狀陣列+dfs

題意 給你一顆樹,由兩種操作: 1.把這棵樹深度為 D D D的點全部都加上一個值。 2.求以p為根節點的子樹的權值和是多少? 思路 對於樹上的東西,我們可以把他求一下DFS序,之後就可以把樹上的結構變成

Codeforces Round #514 (Div. 2) E. Split the Tree貪心+倍增

題意:給你一棵樹,問你最多能把這棵樹分成多少條鏈,使得每條鏈的長度不超過L,每條鏈上的點的權值和不超過S。 思路:這題是參考別人的思路,等有實力了自己再試試。。。從葉子往上貪心,每一次取能達到的最長鏈,也就是儘可能走到最遠的父親那裡,這裡採用樹上倍增處理,用top記錄每個節點最遠能去哪,然後從下

ACM-ICPC 2018 瀋陽賽區網路預賽 J. Ka Chang +樹狀陣列+dfs

題意 給你一顆樹,由兩種操作: 1.把這棵樹深度為DD的點全部都加上一個值。 2.求以p為根節點的子樹的權值和是多少? 思路 對於樹上的東西,我們可以把他求一下DFS序,之後就可以把樹上的結構變成線性的結構,之後就是查詢和修改這兩個東西了。 關於修改