UOJ400/LOJ2553 CTSC2018 暴力写挂 边分治、虚树
跟隔壁通道是一个类型的
要求的式子中有两个LCA,不是很方便,因为事实上在这种题目中LCA一般都是枚举的对象……
第二棵树上的LCA显然是动不了的,因为没有其他的量跟它有关了,于是考虑将\(dep_x+dep_y-dep_{LCA(x,y)}\)魔改一下
它等于\(\frac{1}{2} (dep_x+dep_y+dist_{x,y})\),LCA就没了
然后做法就很明晰了
在第一棵树上边分治,为了叙述方便称实点为原树上的点,虚点为边分治构建过程中加入的点
设边分治到边\((x,y)\),与\(x\)相连的连通块中的实点点集为\(L\),与\(y\)相连的连通块中的实点点集为\(R\)
那么当前边贡献的答案就是\(\max\limits_{i \in L} \max\limits_{j \in R} \frac{1}{2}(dist_i + dist_j + dep_i + dep_j + w(x,y)) - dep'_{LCA'(i,j)}\),其中\(dist_i\)表示\(i\)到边\((x,y)\)的距离
接着考虑枚举\(LCA'(i,j)\)。对\(L \cup R\)在第二棵树上建立虚树进行树形DP,设\(f_{i,0/1}\)表示第二棵子树上\(i\)的子树中且属于\(L/R\)的点集中\(dist + dep\)的最大值,在合并两棵子树的时候贡献答案即可。
为了方便可以将\(w(x,y)\)丢进\(L\)或\(R\)的点权中
注意题目式子中\(x=y\)的情况
还有UOJ基数排序快LOJ快排快到底是个什么鬼
#include<bits/stdc++.h>
//This code is written by Itst
using namespace std;
inline int read(){
int a = 0;
char c = getchar();
bool f = 0;
while(!isdigit(c)){
if(c == '-')
f = 1;
c = getchar();
}
while(isdigit(c)){
a = (a << 3) + (a << 1) + (c ^ '0');
c = getchar();
}
return f ? -a : a;
}
const int MAXN = 3.7e5 + 9;
int N , CCC;
long long val[MAXN] , ans;
namespace Tree2{
struct Edge{
int end , upEd , w;
}Ed[MAXN << 1];
int head[MAXN] , dep[MAXN] , fir[MAXN] , ST[21][MAXN << 1] , logg2[MAXN << 1];
int cntEd , ts;
long long len[MAXN];
inline void addEd(int a , int b , int c){
Ed[++cntEd].end = b;
Ed[cntEd].upEd = head[a];
head[a] = cntEd;
Ed[cntEd].w = c;
}
void dfs(int x , int p){
dep[x] = dep[p] + 1;
ST[0][++ts] = x;
fir[x] = ts;
for(int i = head[x] ; i ; i = Ed[i].upEd)
if(Ed[i].end != p){
len[Ed[i].end] = len[x] + Ed[i].w;
dfs(Ed[i].end , x);
ST[0][++ts] = x;
}
}
inline int cmp(int a , int b){
return dep[a] < dep[b] ? a : b;
}
void init_ST(){
for(int i = 2 ; i <= ts ; ++i)
logg2[i] = logg2[i >> 1] + 1;
for(int i = 1 ; 1 << i <= ts ; ++i)
for(int j = 1 ; j + (1 << i) - 1 <= ts ; ++j)
ST[i][j] = cmp(ST[i - 1][j] , ST[i - 1][j + (1 << (i - 1))]);
}
inline int LCA(int x , int y){
x = fir[x];
y = fir[y];
if(x > y)
swap(x , y);
int t = logg2[y - x + 1];
return cmp(ST[t][x] , ST[t][y - (1 << t) + 1]);
}
int st[MAXN] , top;
long long dp[MAXN][2];
vector < int > v , ch[MAXN];
void init(){
for(int i = 1 ; i < N ; ++i){
int a = read() , b = read() , c = read();
addEd(a , b , c);
addEd(b , a , c);
}
dfs(1 , 0);
init_ST();
memset(dp , -0x3f , sizeof(dp));
ans = dp[0][0];
}
void solve(int x){
for(int i = 0 ; i < ch[x].size() ; ++i){
solve(ch[x][i]);
ans = max(ans , max(dp[x][0] + dp[ch[x][i]][1] , dp[x][1] + dp[ch[x][i]][0]) / 2 - len[x]);
dp[x][0] = max(dp[x][0] , dp[ch[x][i]][0]);
dp[x][1] = max(dp[x][1] , dp[ch[x][i]][1]);
dp[ch[x][i]][0] = dp[ch[x][i]][1] = dp[0][0];
}
ch[x].clear();
}
bool cmp1(int a , int b){
return fir[a] < fir[b];
}
int pot[11] , now[MAXN] , tmp[MAXN];
void bSort(){
int times = 1 , l = v.size();
for(int i = 0 ; i < l ; ++i)
now[i] = v[i];
for(int i = 0 ; i <= 6 ; ++i){
for(int j = 0 ; j <= 10 ; ++j)
pot[j] = 0;
for(int j = 0 ; j < l ; ++j)
++pot[(fir[now[j]] / times) % 10 + 1];
for(int j = 1 ; j <= 10 ; ++j)
pot[j] += pot[j - 1];
for(int j = 0 ; j < l ; ++j)
tmp[pot[(fir[now[j]] / times) % 10]++] = now[j];
memcpy(now , tmp , sizeof(int) * l);
times *= 10;
}
v.clear();
for(int i = 0 ; i < l ; ++i)
v.push_back(now[i]);
}
void work(const vector < int >& nd1 , const vector < int >& nd2 , int c){
++CCC;
for(int i = 0 ; i < nd1.size() ; ++i)
dp[nd1[i]][0] = val[nd1[i]] + c;
for(int i = 0 ; i < nd2.size() ; ++i)
dp[nd2[i]][1] = val[nd2[i]];
v.clear();
v.insert(v.end() , nd1.begin() , nd1.end());
v.insert(v.end() , nd2.begin() , nd2.end());
bSort();
//sort(v.begin() , v.end() , cmp1);
top = 0;
for(int i = 0 ; i < v.size() ; ++i){
if(top){
int p = LCA(st[top] , v[i]);
while(dep[st[top - 1]] >= dep[p]){
ch[st[top - 1]].push_back(st[top]);
--top;
}
if(dep[st[top]] > dep[p]){
ch[p].push_back(st[top]);
st[top] = p;
}
}
st[++top] = v[i];
}
while(top > 1){
ch[st[top - 1]].push_back(st[top]);
--top;
}
solve(st[1]);
dp[st[1]][0] = dp[st[1]][1] = dp[0][0];
top = 0;
}
}
namespace Tree1{
#define PII pair < int , int >
#define st first
#define nd second
struct Edge{
int end , upEd , w;
}Ed[MAXN << 2];
vector < PII > ch[MAXN];
int head[MAXN << 1] , cntN , cntEd = 1 , nowSz , minSz , minInd;
long long len[MAXN];
inline void addEd(int a , int b , int c){
Ed[++cntEd].end = b;
Ed[cntEd].upEd = head[a];
head[a] = cntEd;
Ed[cntEd].w = c;
}
void rebuild(int x , int p){
int cur = x;
for(int i = 0 ; i < ch[x].size() ; ++i)
if(ch[x][i].st != p){
len[ch[x][i].st] = len[x] + ch[x][i].nd;
int t = ++cntN;
addEd(cur , t , 0);
addEd(t , cur , 0);
addEd(t , ch[x][i].st , ch[x][i].nd);
addEd(ch[x][i].st , t , ch[x][i].nd);
cur = t;
rebuild(ch[x][i].st , x);
}
}
void init(){
for(int i = 1 ; i < N ; ++i){
int a = read() , b = read() , c = read();
ch[a].push_back(PII(b , c));
ch[b].push_back(PII(a , c));
}
cntN = N;
rebuild(1 , 0);
}
bool vis[MAXN << 1];
void getSz(int x){
vis[x] = 1;
++nowSz;
for(int i = head[x] ; i ; i = Ed[i].upEd)
if(!vis[Ed[i].end])
getSz(Ed[i].end);
vis[x] = 0;
}
int getRt(int x){
vis[x] = 1;
int sz = 1;
for(int i = head[x] ; i ; i = Ed[i].upEd)
if(!vis[Ed[i].end]){
int t = getRt(Ed[i].end);
if(minSz > max(t , nowSz - t)){
minSz = max(t , nowSz - t);
minInd = i;
}
sz += t;
}
vis[x] = 0;
return sz;
}
void getNd(int x , long long l , vector < int > &v){
if(x <= N){
val[x] = l + len[x];
v.push_back(x);
}
vis[x] = 1;
for(int i = head[x] ; i ; i = Ed[i].upEd)
if(!vis[Ed[i].end])
getNd(Ed[i].end , l + Ed[i].w , v);
vis[x] = 0;
}
vector < int > nd1 , nd2;
void solve(int x){
nowSz = 0;
minSz = 1e9;
getSz(x);
if(nowSz == 1)
return;
getRt(x);
int p = Ed[minInd].end , q = Ed[minInd ^ 1].end;
Ed[minInd].end = q;
Ed[minInd ^ 1].end = p;
nd1.clear();
nd2.clear();
getNd(p , 0 , nd1);
getNd(q , 0 , nd2);
if(nd1.size() && nd2.size())
Tree2::work(nd1 , nd2 , Ed[minInd].w);
solve(p);
solve(q);
}
void work(){
solve(1);
for(int i = 1 ; i <= N ; ++i)
ans = max(ans , len[i] - Tree2::len[i]);
}
}
signed main(){
#ifndef ONLINE_JUDGE
freopen("in" , "r" , stdin);
//freopen("out" , "w" , stdout);
#endif
N = read();
Tree1::init();
Tree2::init();
Tree1::work();
cout << ans;
return 0;
}