GYM 102331 J Jiry Matchings
GYM 102331 J Jiry Matchings
一个比较显然的\(dp\)。
\(dp_{i,k,j}\)表示以i为根的子树内有\(k\)个匹配,且第i个点的使用状态为j(用/不用)。
转移相当于做一遍\(max+\)卷积。由于这个显然是凸的,所以可以做到线性计算\(max+\)卷积。
然后可以想到分治合并子树信息。
但是考虑链的情况,时间复杂度会退化成\(O(N^2)\)。
所以可以使用一个trick:重链剖分。
每次将重链取出来,然后分治算其它节点的贡献。
然后将贡献归到重链上,分治计算重链。
分析时间复杂度:
更具重链剖分的性质。
每一个点会被算\(O(\log n)\)次。
然后分治又是一个log。
所以是\(O(N\log^2N)\)。
code:
/*
{
######################
# Author #
# Gary #
# 2021 #
######################
*/
#include<bits/stdc++.h>
#define rb(a,b,c) for(int a=b;a<=c;++a)
#define rl(a,b,c) for(int a=b;a>=c;--a)
#define LL long long
#define IT iterator
#define PB push_back
#define II(a,b) make_pair(a,b)
#define FIR first
#define SEC second
#define FREO freopen("check.out","w",stdout)
#define rep(a,b) for(int a=0;a<b;++a)
#define SRAND mt19937 rng(chrono::steady_clock::now().time_since_epoch().count())
#define random(a) rng()%a
#define ALL(a) a.begin(),a.end()
#define POB pop_back
#define ff fflush(stdout)
#define fastio ios::sync_with_stdio(false)
#define check_min(a,b) a=min(a,b)
#define check_max(a,b) a=max(a,b)
using namespace std;
const int INF=0x3f3f3f3f;
typedef pair<int,int> mp;
/*}
*/
const int MAXN=200000+23;
int n;
vector<mp> g[MAXN];
struct poly{
vector<LL> v;
poly(int n){v.resize(n);}
poly(){}
// void print(){
// cout<<v.size()<<":";
// for(auto it:v){
// cout<<it<<",";
// }
// cout<<endl;
// }
poly operator * (poly oth){//max + 卷积
if(oth.v.empty()) oth.v.PB(-1e15);
if(v.empty()) v.PB(-1e15);
int deg=oth.v.size()+v.size()-1;
poly ret=poly(deg);
int A=1,B=1;
LL presum=v[0]+oth.v[0];
ret.v[0]=presum;
rb(i,1,ret.v.size()-1){
LL RetA,RetB;
if(A>=v.size()){
RetA=-1e18;
}
else{
RetA=v[A]-v[A-1];
}
if(B>=oth.v.size()){
RetB=-1e18;
}
else{
RetB=oth.v[B]-oth.v[B-1];
}
// cout<<i<<" "<<A<<' '<<B<<" "<<RetA<<" "<<RetB<<' '<<endl;
ret.v[i]=max(RetA,RetB);
presum+=ret.v[i];
ret.v[i]=presum;
if(RetA>RetB) ++A;
else ++B;
}
// (*this).print();
// oth.print();
// ret.print();
// cout<<endl;
return ret;
}
};
void expand(poly &p,int sz){
while(p.v.size()<sz){
p.v.PB(-1e15);
}
}
const int MOD=9982443;
mp is[400000+233];
int val[400000+233],top[MOD],nxt[400000+233];
int cnt=0;
void insert(mp A,int val_){
LL ha=1ll*A.FIR*A.SEC+(A.FIR^A.SEC);
ha%=MOD;
++cnt;
val[cnt]=val_;
is[cnt]=A;
nxt[cnt]=top[ha];
top[ha]=cnt;
}
int query(mp A){
LL ha=1ll*A.FIR*A.SEC+(A.FIR^A.SEC);
ha%=MOD;
for(int now=top[ha];now;now=nxt[now]){
if(is[now]==A) return val[now];
}
assert(0);
return 19260817;
}
void chk_max(poly & A,poly B){
int sz=B.v.size();
expand(A,sz);
rep(i,sz){
check_max(A.v[i],B.v[i]);
}
}
struct chain{
poly ret[2][2];
chain(){}
chain(poly A,poly B,poly C,poly D){ret[0][0]=A,ret[0][1]=B,ret[1][0]=C,ret[1][1]=D;}
chain merge (pair<chain,int> p,bool t1=0,bool t2=0){
chain oth=p.FIR;
int va=p.SEC;
poly A[2][2],B[2][2];
rep(i,2) rep(j,2) A[i][j]=ret[i][j],B[i][j]=oth.ret[i][j];
poly rest[2][2];
rep(i,2) rep(j,2) rep(i2,2) rep(j2,2){
if(i!=j){
if(t1) continue;
}
if(j2!=i2){
if(t2) continue;
}
poly tmp=A[i][j]*B[i2][j2];
expand(rest[i][j2],tmp.v.size());
if((!j)&&(!i2))
expand(rest[i|t1][j2|t2],tmp.v.size()+1);
rep(z,tmp.v.size()){
if((!j)&&(!i2)){
check_max(rest[i|t1][j2|t2].v[z+1],tmp.v[z]+va);
}
check_max(rest[i][j2].v[z],tmp.v[z]);
}
}
return chain(rest[0][0],rest[0][1],rest[1][0],rest[1][1]);
}
};
int find_mid(vector<pair<poly,poly> > v){
int all=0;
for(auto it:v) all+=it.SEC.v.size()+it.FIR.v.size();
mp tmp=II(INF,INF);
int sum=0;
rep(i,v.size()){
sum+=v[i].FIR.v.size()+v[i].SEC.v.size();
check_min(tmp,II(abs(all/2-sum),i));
}
return tmp.SEC;
}
int siz[MAXN],heavy_son[MAXN];
bool online[MAXN];
pair<poly,poly> solve(int now,int pre);
pair<poly,poly> merging(int now,vector<pair<poly,poly> > v,vector<int> son){
if(v.empty()) return {poly(1),poly()};
if(v.size()==1){
poly A=v[0].FIR,B=v[0].FIR;
chk_max(B,v[0].SEC);
int add=query(II(son[0],now));
A.v.PB(-1e15);
A.v[0]=-1e15;
rep(i,v[0].FIR.v.size()){
A.v[i+1]=add+v[0].FIR.v[i];
}
return II(B,A);
}
int mid=find_mid(v);
poly A,B;
vector<pair<poly,poly > > L,R;
vector<int> Ls,Rs;
rb(i,0,mid){
L.PB(v[i]);
Ls.PB(son[i]);
}
rb(i,mid+1,v.size()-1){
R.PB(v[i]);
Rs.PB(son[i]);
}
pair<poly,poly> Rl,Rr;
Rl=merging(now,L,Ls);
Rr=merging(now,R,Rs);
A=Rl.FIR*Rr.FIR;
B=Rl.SEC*Rr.FIR;
chk_max(B,Rl.FIR*Rr.SEC);
return II(A,B);
}
pair<poly,poly> work(int now){
vector<pair<poly,poly> > v;
vector<int> son;
for(auto it:g[now]){
if(!online[it.FIR]){
v.PB(solve(it.FIR,now));
son.PB(it.FIR);
}
}
return merging(now,v,son);
}
chain work(vector<pair<poly,poly> > v,vector<int> heavy_chain){
if(v.size()==1){
return chain(v[0].FIR,poly(),poly(),v[0].SEC);
}
int md=find_mid(v);
vector<pair<poly,poly> > l,r;
vector<int> cl,cr;
rb(i,0,md){
cl.PB(heavy_chain[i]);
l.PB(v[i]);
}
int Addi=query(II(heavy_chain[md],heavy_chain[md+1]));
rb(i,md+1,v.size()-1){
cr.PB(heavy_chain[i]);
r.PB(v[i]);
}
chain A,B;
A=work(l,cl);
B=work(r,cr);
// cout<<Addi<<endl;
chain rest=A.merge(II(B,Addi),(l.size()==1),(r.size()==1));
// for(auto it:heavy_chain){
// cout<<"*"<<it<<' ';
// }
// cout<<endl;
// rest.ret[1][1].print();
return rest;
}
void getheavy(int now,int pre){
siz[now]=1;
heavy_son[now]=0;
for(auto it:g[now]){
int nex=it.FIR;
if(nex==pre) continue;
getheavy(nex,now);
siz[now]+=siz[nex];
if(siz[nex]>siz[heavy_son[now]]) heavy_son[now]=nex;
}
}
pair<poly,poly> solve(int now,int pre=0){
getheavy(now,pre);
vector<int> heavy_line;
int stand=now;
while(stand){
heavy_line.PB(stand);
online[stand]=true;
stand=heavy_son[stand];
}
vector<pair<poly,poly> > vp;
for(auto it:heavy_line){
vp.PB(work(it));
}
// cout<<"__"<<now<<' '<<pre<<endl;
// for(auto it:vp){
// it.FIR.print();
// cout<<",\n";
// it.SEC.print();
// cout<<endl;
// }
chain ret=work(vp,heavy_line);
pair<poly,poly> rest;
rest.FIR=ret.ret[0][0];
chk_max(rest.FIR,ret.ret[0][1]);
rest.SEC=ret.ret[1][0];
chk_max(rest.SEC,ret.ret[1][1]);
// cout<<"##"<<now<<" "<<pre<<endl;
// ret.ret[0][1].print();
return rest;
}
int main(){
// poly A,B;
// A.v.PB(-1e15);
// A.v.PB(5);
// B.v.PB(-1e15);
// B.v.PB(4);
// A=A*B;
// A.print();
// return 0;
// chain A,B;
// A=chain(poly(1),poly(),poly(),poly());
// B=chain(poly(1),poly(),poly(),poly());
// A=A+II(B,1);
// A.ret[0][0].print();
// return 0;
scanf("%d",&n);
rb(i,1,n-1){
int u,v,w;
scanf("%d%d%d",&u,&v,&w);
g[u].PB(II(v,w));
g[v].PB(II(u,w));
insert(II(u,v),w);
insert(II(v,u),w);
}
pair<poly,poly> ret=solve(1);
expand(ret.SEC,n);
expand(ret.FIR,n);
chk_max(ret.FIR,ret.SEC);
rb(i,1,n-1){
if(ret.FIR.v[i]<-2e14){
printf("? ");
}
else{
printf("%lld ",ret.FIR.v[i]);
}
}
return 0;
}
/*
4
1 2 4
4 3 5
1 4 6
*/