BZOJ4860 BJOI2017 树的难题 点分治、线段树合并

传送门


只会线段树……关于单调队列的解法可以去看“重建计划”一题。

看到路径长度$\in [L,R]$考虑点分治。可以知道,在当前分治中心向其他点的路径中,始边(也就是分治中心到对应子树的根的那一条边)颜色相同的两条路径在拼合的时候在加上两条路径的权值之后需要减掉始边颜色的权值(因为被计算了两次),而初始边颜色不同的进行拼合就直接将两条路径的权值加起来即可。我们考虑分开维护这两种拼合。

在每一个分治中心里,我们对其引出的边按照颜色排序(目的是使得始边颜色相同的若干子树放在一起统一遍历),维护两个线段树,一个维护之前遍历过的子树中初始边颜色与当前子树不同的子树中每种长度的路径的最大权值,另一个维护之前遍历过的子树中初始边颜色与当前子树相同的子树中每种长度的路径的最大权值。

那么我们每一次遍历完一棵子树,在线段树中对于每一种长度的路径在线段树上查询可以拼合的长度的路径的最大权值,记得在第二个线段树中询问到的答案需要减去始边颜色的权值,然后用这一棵子树的路径更新第二个线段树。每一次遍历完一种颜色就把第二个线段树合并到第一个线段树内即可。复杂度$O(nlog^2n)$

  1 #include
  2 #define INF 0x7fffffff
  3 #define int long long
  4 #define mid ((l + r) >> 1)
  5 //This code is written by Itst
  6 using namespace std;
  7 
  8 inline int read(){
  9     int a = 0;
 10     bool f = 0;
 11     char c = getchar();
 12     while(c != EOF && !isdigit(c)){
 13         if(c == '-')
 14             f = 1;
 15         c = getchar();
 16     }
 17     while(c != EOF && isdigit(c)){
 18         a = (a << 3) + (a << 1) + (c ^ '0');
 19         c = getchar();
 20     }
 21     return f ? -a : a;
 22 }
 23 
 24 const int MAXN = 200010;
 25 vector < pair < int , int > > Edge[MAXN];
 26 struct node{
 27     int l , r , maxN;
 28 }Tree[MAXN * 30];
 29 int N , M , L , R , ans , cntNode , nowSize , minSize , minInd , roota , rootb;
 30 int val[MAXN] , size[MAXN] , sz[MAXN] , mx[MAXN];
 31 bool vis[MAXN];
 32 
 33 inline int allocate(){
 34     int t = ++cntNode;
 35     Tree[t].l = Tree[t].r = 0;
 36     Tree[t].maxN = -INF;
 37     return t;
 38 }
 39 
 40 inline void pushup(int x){
 41     Tree[x].maxN = max(Tree[Tree[x].l].maxN , Tree[Tree[x].r].maxN);
 42 }
 43 
 44 inline int max(int a , int b){
 45     return a > b ? a : b;
 46 }
 47 
 48 int merge(int p , int q){
 49     if(!p)
 50         return q;
 51     if(!q)
 52         return p;
 53     Tree[p].maxN = max(Tree[p].maxN , Tree[q].maxN);
 54     Tree[p].l = merge(Tree[p].l , Tree[q].l);
 55     Tree[p].r = merge(Tree[p].r , Tree[q].r);
 56     return p;
 57 }
 58 
 59 void insert(int& now , int l , int r , int tar , int num){
 60     if(!now)
 61         now = allocate();
 62     if(l == r)
 63         Tree[now].maxN = max(Tree[now].maxN , num);
 64     else{
 65         if(mid >= tar)
 66             insert(Tree[now].l , l , mid , tar , num);
 67         else
 68             insert(Tree[now].r , mid + 1 , r , tar , num);
 69         pushup(now);
 70     }
 71 }
 72 
 73 int query(int now , int l , int r , int L , int R){
 74     if(l >= L && r <= R)
 75         return Tree[now].maxN;
 76     if(!now)
 77         return -INF;
 78     int maxN = -INF;
 79     if(mid >= L)
 80         maxN = max(maxN , query(Tree[now].l , l , mid , L , R));
 81     if(mid < R)
 82         maxN = max(maxN , query(Tree[now].r , mid + 1 , r , L , R));
 83     return maxN;
 84 }
 85 
 86 void getSize(int x){
 87     vis[x] = 1;
 88     ++nowSize;
 89     for(int i = 0 ; i < sz[x] ; ++i){
 90         int t = Edge[x][i].second;
 91         if(!vis[t])
 92             getSize(t);
 93     }
 94     vis[x] = 0;
 95 }
 96 
 97 void getRoot(int x){
 98     size[x] = vis[x] = 1;
 99     int maxN = 0;
100     for(int i = 0 ; i < sz[x] ; ++i){
101         int t = Edge[x][i].second;
102         if(!vis[t]){
103             getRoot(t);
104             size[x] += size[t];
105             maxN = max(maxN , size[t]);
106         }
107     }
108     maxN = max(maxN , nowSize - size[x]);
109     if(maxN < minSize){
110         minSize = maxN;
111         minInd = x;
112     }
113     vis[x] = 0;
114 }
115 
116 void dfs(int x , int c , int len , int nowCol){
117     if(len > R)
118         return;
119     vis[x] = 1;
120     mx[len] = max(mx[len] , c);
121     for(int i = 0 ; i < sz[x] ; ++i){
122         int t = Edge[x][i].second;
123         if(!vis[t])
124             dfs(Edge[x][i].second , nowCol == Edge[x][i].first ? c : c + val[Edge[x][i].first] , len + 1 , Edge[x][i].first);
125     }
126     vis[x] = 0;
127 }
128 
129 void solve(int x){
130     nowSize = cntNode = roota = rootb = 0;
131     minSize = INF;
132     getSize(x);
133     getRoot(x);
134     int root = minInd;
135     getSize(root);
136     vis[root] = 1;
137     for(int i = 0 ; i < sz[root] ; ++i){
138         int t = Edge[root][i].second , r = Edge[root][i].first;
139         if(!vis[t]){
140             memset(mx , -0x3f , sizeof(int) * (size[t] + 1));
141             if(rootb && r != Edge[root][i - 1].first){
142                 roota = merge(roota , rootb);
143                 rootb = 0;
144             }
145             dfs(t , val[r] , 1 , r);
146             for(int i = 1 ; i <= size[t] && i < R && mx[i] != mx[0] ; ++i)
147                 ans = max(max(ans , i >= L && i <= R ? mx[i] : -INF) , max(query(roota , 1 , R , max(1 , L - i) , R - i) , query(rootb , 1 , R , max(1 , L - i) , R - i) - val[r]) + mx[i]);
148             ans = max(ans , mx[R]);
149             for(int i = 1 ; i <= size[t] && i < R && mx[i] != mx[0] ; ++i)
150                 insert(rootb , 1 , R , i , mx[i]);
151         }
152     }
153     for(int i = 0 ; i < sz[root] ; ++i){
154         int t = Edge[root][i].second;
155         if(!vis[t])
156             solve(t);
157     }
158 }
159 
160 signed main(){
161 #ifndef ONLINE_JUDGE
162     freopen("3714.in" , "r" , stdin);
163     //freopen("3714.out" , "w" , stdout);
164 #endif
165     Tree[0].maxN = ans = -INF;
166     N = read();
167     M = read();
168     L = read();
169     R = read();
170     for(int i = 1 ; i <= M ; ++i)
171         val[i] = read();
172     for(int i = 1 ; i < N ; ++i){
173         int a = read() , b = read() , c = read();
174         Edge[a].push_back(make_pair(c , b));
175         Edge[b].push_back(make_pair(c , a));
176         ++sz[a];
177         ++sz[b];
178     }
179     for(int i = 1 ; i <= N ; ++i)
180         sort(Edge[i].begin() , Edge[i].end());
181     solve(1);
182     cout << ans;
183     return 0;
184 }

转载于:https://www.cnblogs.com/Itst/p/10074845.html

你可能感兴趣的:(BZOJ4860 BJOI2017 树的难题 点分治、线段树合并)