Codechef:Painting Tree/KILLER(二进制分组)

传送门

题解:
这道题,一开始想到自然是线段树合并了,每个点维护个 f i , j f_{i,j} 表示从 j j 开始上到 i i ,其他内部配对的最小值。不过这样就要支持区间加二次函数求最大值,根据bzoj某道题的经验,显然是不能在线段树上搞的。

然后仔细观察一下,发现这个 ( h d e p x ) (h-dep_x) 连续,进一步发现,这是一个关于 d e p i dep_i 的二次函数!所以启发式合并之后,我们只需要维护:一堆二次函数集体加上一个数,插入一个二次函数,维护二次函数在 x x 点的最小值。

插入不好做,不过合并两堆大小相近的二次函数集合下凸包可以做到 O ( n + m ) O(n+m) (当然这里我不太确定,因为二次函数有两个交点,可能还要乘个2,这样复杂度就不对了,不过网上都说是 O ( n ) O(n) 的,那就当他 O ( n ) O(n) 的吧),我们只需要做一下二进制分组,合并的时候类似归并排序一样,对每个分隔点下方的两个函数求交点取个 m i n min 就好了。

时间复杂度 O ( n log 2 n ) O(n \log^2 n) ,注意细节:
1.二次函数系数只会出现0.5,为了避免精度问题,先乘个2。
2.算交点用判别式可能会爆long long,先转成double计算。
3.分界点存long long(如果是小数则存后面的那一个整数)。

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;

const LL inf=2e18;
const int RLEN=1<<18|1;
inline char nc() {
	static char ibuf[RLEN],*ib,*ob;
	(ib==ob) && (ob=(ib=ibuf)+fread(ibuf,1,RLEN,stdin));
	return (ib==ob) ? -1 : *ib++;
}
inline LL rd() {
	char ch=nc(); LL i=0,f=1;
	while(!isdigit(ch)) {if(ch=='-')f=-1; ch=nc();}
	while(isdigit(ch)) {i=(i<<1)+(i<<3)+ch-'0'; ch=nc();}
	return i*f;
}

const int N=2e5+50;

int n,T,dep[N],sze[N],son[N]; 
LL c[N],h[N];
vector <int> g[N];
struct func {
	LL a,b,c;
	func() {}
	func(LL a,LL b,LL c) : a(a),b(b),c(c) {}
	friend inline bool operator ==(const func &a,const func &b) {
		return (a.a==b.a && a.b==b.b && a.c==b.c);
	}
	inline LL f(LL v) {return a*v*v+b*v+c;}
};
struct node {
	LL p;
	func f;
	node() {}
	node(LL p,func f) : p(p),f(f) {}
	friend inline bool operator ==(const node &a,const node &b) {return a.f==b.f;}
};
struct atom {
	vector <node> det;
	vector <func> alf;
	atom() {}
	atom(func v) {
		det.push_back(node(1,v));
		alf.push_back(v);
	}
	inline LL mx(int pos) {
		int l=0, r=det.size()-1,ans=0;
		while(l<=r) {
			int mid=(l+r)>>1;
			if(det[mid].p<=pos) ans=mid, l=mid+1;
			else r=mid-1;
		} return det[ans].f.f(pos);
	}
};
inline LL calc(func a,func b,LL lim) {
	func c; c.a=b.a-a.a; c.b=b.b-a.b; c.c=b.c-a.c;
	LL ans=inf;
	if(c.a) {
		double det=(double)c.b*c.b-4*(double)c.a*c.c;
		if(det<0) return inf;
		double x=(-c.b+sqrt(det))/2/c.a, y=(-c.b-sqrt(det))/2/c.a;
		if(x>lim && x<inf) ans=min(ans,(long long)ceil(x));
		if(y>lim && x<inf) ans=min(ans,(long long)ceil(y));
	} else if(c.b) {
		double x=-(double)c.c/c.b;
		if(x>lim && x<inf) ans=min(ans,(long long)ceil(x));
	} return ans;
}
inline atom merge(atom &a,atom &b) {
	atom c; vector <node> tp;
	for(auto v:a.alf) c.alf.push_back(v);
	for(auto v:b.alf) c.alf.push_back(v);
	int x=0,y=0;
	while(x<a.det.size() && y<b.det.size()) {
		if(a.det[x].p<b.det[y].p) tp.push_back(a.det[x++]);
		else tp.push_back(b.det[y++]);
	}
	while(x<a.det.size()) tp.push_back(a.det[x++]);
	while(y<b.det.size()) tp.push_back(b.det[y++]);
	
	x=0; y=0;
	for(int i=0;i<tp.size();i++) {
		while(x<a.det.size()-1 && a.det[x+1].p<=tp[i].p) ++x;
		while(y<b.det.size()-1 && b.det[y+1].p<=tp[i].p) ++y;
		LL l=tp[i].p, r=(i<tp.size()-1) ? tp[i+1].p : inf;
		func u=a.det[x].f, v=b.det[y].f;
		while(l<r) {
			if(l>T) break;
			if(u==v) {
				c.det.push_back(node(l,u));
				break;
			}
			LL m=calc(u,v,l);
			LL v1=u.f(l), v2=v.f(l);
			if(v1!=v2) c.det.push_back(node(l,v1>v2 ? v : u));
			else {
				v1=(m==inf ? u.f(T+2) : u.f((l+m-1)/2));
				v2=(m==inf ? v.f(T+2) : v.f((l+m-1)/2));
				c.det.push_back(node(l,v1>v2 ? v : u));
			}
			l=m;
		}
	}
	c.det.erase(unique(c.det.begin(),c.det.end()),c.det.end());
	return c;
}
struct bunch {
	LL tag;
	vector <atom> cont;
	bunch() {tag=0;}
	inline void add(func v) {
		v.c-=tag;
		atom t=atom(v);
		while(cont.size() && cont.back().alf.size()==t.alf.size()) 
			t=merge(t,cont.back()), cont.pop_back();
		cont.push_back(t);
	}
	inline LL mx(int pos) {
		LL t=inf;
		for(auto &v:cont) t=min(t,v.mx(pos));
		return t+tag;
	}
} *f[N];

inline void dfs(int x,int fa) {
	sze[x]=1; son[x]=0; f[x]=NULL;
	for(auto v:g[x]) if(v^fa) {
		dfs(v,x); sze[x]+=sze[v];
		if(sze[son[x]]<sze[v]) son[x]=v;
	} 
	LL sum=0;
	for(auto v:g[x]) 
		if(v^fa) sum+=f[v]->mx(dep[x]+1);
	if(son[x]) {
		f[x]=f[son[x]];
		LL d=sum-f[x]->mx(dep[x]+1);
		f[x]->tag+=d;
	}
	if(!f[x]) f[x]=new bunch;
	f[x]->add(func(c[x],-T*c[x]*2-c[x]-2*c[x]*c[x],2*T*c[x]*dep[x]+2*T*c[x]-c[x]*dep[x]*dep[x]-c[x]*dep[x]+2*(dep[x]+1)*c[x]*c[x]-2*h[x]+sum));
	LL prev=f[x]->mx(dep[x]);
	for(auto u:g[x]) if(u^fa && u^son[x]) {
		for(atom &v:f[u]->cont) 
			for(func t:v.alf) {
				func y=t; y.c+=f[u]->tag;
				y.c+=sum-f[u]->mx(dep[x]+1);
				f[x]->add(y);
			}
	}
}
inline void pre(int x,int f) {
	dep[x]=dep[f]+1;
	for(auto v:g[x]) if(v^f) pre(v,x);
	T=max(T,dep[x]);
}
inline void solve() {
	n=rd(); T=0;
	for(int i=1;i<=n;i++) 
		h[i]=rd(), c[i]=rd();
	for(int i=1;i<=n;i++) g[i].clear();
	for(int i=1;i<n;i++) {
		int x=rd(), y=rd();
		g[x].push_back(y);
		g[y].push_back(x);
	} 
	pre(1,0); dfs(1,0);
	cout<<(long long)(f[1]->mx(1)/2)<<'\n';
}
int main() {
	for(int T=rd();T;T--) solve();
}
发布了553 篇原创文章 · 获赞 227 · 访问量 24万+

猜你喜欢

转载自blog.csdn.net/qq_35649707/article/details/84031351