zoukankan      html  css  js  c++  java
  • BZOJ 2402 陶陶的难题II (树链剖分、线段树、凸包、分数规划)

    毒瘤,毒瘤,毒瘤……

    (30000)这个数据范围,看上去就是要搞事的啊。。。

    题目链接: https://www.lydsy.com/JudgeOnline/problem.php?id=2402

    题解:

    首先化式子: 假设二分的答案为(mid)(frac{y_i+q_j}{x_i+p_j}ge mid, y_i+q_jge mid(x_i+p_j), y_i-mid imes x_i+q_j-mid imes p_jge 0)

    那么我们实际上就是要找一个(i)使得(-x_i imes mid+y_i)最大,同理找一个(j)使得(-p_j imes mid+q_j)最大,这两个任务完全一样,现在就讨论前者

    为了方便(强迫症)让我们把它变成(-a_ix+b_i)的形式

    仿照斜率优化的思路,如果(a_i<a_j),那么(i)不劣于(j)当且仅当(-a_ix+b_ige -a_jx+b_j, xge frac{b_j-b_i}{a_j-a_i}).

    那么假设有并排着的三个点(i,j,k,a_i<a_j<a_k), 则(i)不劣于(j)当且仅当(xge frac{b_j-b_i}{a_j-a_i}), (k)不劣于(j)当且仅当(xle frac{b_k-b_j}{a_k-a_j}), 如果(frac{b_j-b_i}{a_j-a_i}le frac{b_k-b_j}{a_k-a_j}), 那么对于任何(x)上面两个条件至少有一个成立,那么(j)就是无用的。也就是说有用的点一定满足斜率递减,在上凸壳的左半边上。

    这题没有修改操作,那么我们可以用线段树求出每个子区间内的凸壳,然后对于每个询问,先分数规划二分答案,然后求树链剖分划分成的每一段区间的最大值,这个可以通过在线段树每个子区间的凸壳上二分得到。

    时间复杂度(O(nlog^4n))

    (这可能是我最近写的几道题唯一一道跑进BZOJ前一半的?23333)

    代码

    #include<cstdio>
    #include<cstdlib>
    #include<cstring>
    #include<cassert>
    #include<vector>
    using namespace std;
    
    const int N = 3e4;
    const double EPS = 1e-8;
    const double INF = 1e6;
    int dcmp(double x) {return x<-EPS ? -1 : (x>EPS ? 1 : 0);}
    struct Point
    {
    	double x,y;
    	Point() {}
    	Point(double _x,double _y) {x = _x,y = _y;}
    	bool operator <(const Point &arg) const {return dcmp(x-arg.x)<0 || (dcmp(x-arg.x)==0 && dcmp(y-arg.y)<0);}
    };
    struct Edge
    {
    	int v,w,nxt;
    } e[(N<<1)+3];
    int fe[N+3];
    int fa[N+3];
    int dfn[N+3];
    int idfn[N+3];
    int sz[N+3];
    int tpn[N+3];
    int dep[N+3];
    int hvs[N+3];
    double a1[N+3],b1[N+3],a2[N+3],b2[N+3];
    int n,q,en,cnt;
    
    struct SegmentTree
    {
    	vector<Point> sgt[(N<<2)+3];
    	void CHpush(int id,Point x)
    	{
    		int j = sgt[id].size()-1;
    		while(j>0 && dcmp((sgt[id][j].y-sgt[id][j-1].y)*(x.x-sgt[id][j].x)-(sgt[id][j].x-sgt[id][j-1].x)*(x.y-sgt[id][j].y))<=0)
    		{
    			sgt[id].pop_back();
    			j--;
    		}
    		sgt[id].push_back(x);
    	}
    	void build(int rtn,int le,int ri,double a[],double b[])
    	{
    		if(le==ri)
    		{
    			sgt[rtn].push_back(Point(a[idfn[le]],b[idfn[le]]));
    			return;
    		}
    		int mid = (le+ri)>>1;
    		build(rtn<<1,le,mid,a,b);
    		build(rtn<<1|1,mid+1,ri,a,b);
    		int i = 0,j = 0;
    		while(i<sgt[rtn<<1].size() && j<sgt[rtn<<1|1].size())
    		{
    			if(sgt[rtn<<1][i]<sgt[rtn<<1|1][j]) {CHpush(rtn,sgt[rtn<<1][i]); i++;}
    			else {CHpush(rtn,sgt[rtn<<1|1][j]); j++;}
    		}
    		while(i<sgt[rtn<<1].size()) {CHpush(rtn,sgt[rtn<<1][i]); i++;}
    		while(j<sgt[rtn<<1|1].size()) {CHpush(rtn,sgt[rtn<<1|1][j]); j++;}
    	}
    	double calc(int rtn,double x)
    	{
    		int left = 0,right = sgt[rtn].size()-1;
    		while(left<right)
    		{
    			int mid = (left+right+1)>>1;
    			if(mid==0) {break;}
    			if(dcmp(x*(sgt[rtn][mid].x-sgt[rtn][mid-1].x)-(sgt[rtn][mid].y-sgt[rtn][mid-1].y))<=0) {left = mid;}
    			else {right = mid-1;}
    		}
    		double ret = -sgt[rtn][left].x*x+sgt[rtn][left].y;
    		return ret;
    	}
    	double query(int rtn,int le,int ri,int lb,int rb,double x)
    	{
    		if(le>=lb && ri<=rb) {return calc(rtn,x);}
    		int mid = (le+ri)>>1; double ret = -INF;
    		if(lb<=mid) {ret = max(ret,query(rtn<<1,le,mid,lb,rb,x));}
    		if(rb>mid) {ret = max(ret,query(rtn<<1|1,mid+1,ri,lb,rb,x));}
    		return ret;
    	}
    } sgt1,sgt2;
    
    void addedge(int u,int v)
    {
    	en++; e[en].v = v;
    	e[en].nxt = fe[u]; fe[u] = en;
    }
    
    void dfs1(int u)
    {
    	sz[u] = 1; hvs[u] = 0;
    	for(int i=fe[u]; i; i=e[i].nxt)
    	{
    		int v = e[i].v;
    		if(v==fa[u]) continue;
    		fa[v] = u;
    		dep[v] = dep[u]+1;
    		dfs1(v);
    		sz[u] += sz[v];
    		if(sz[v]>sz[hvs[u]]) {hvs[u] = v;}
    	}
    }
    
    void dfs2(int u)
    {
    	cnt++; dfn[u] = cnt; idfn[cnt] = u;
    	if(hvs[u]) {tpn[hvs[u]] = tpn[u]; dfs2(hvs[u]);}
    	for(int i=fe[u]; i; i=e[i].nxt)
    	{
    		int v = e[i].v;
    		if(v==hvs[u] || v==fa[u]) continue;
    		tpn[v] = v;
    		dfs2(v);
    	}
    }
    
    bool query(int u,int v,double x)
    {
    	double ret1 = -INF,ret2 = -INF;
    	while(tpn[u]!=tpn[v])
    	{
    		if(dep[fa[tpn[u]]]>dep[fa[tpn[v]]])
    		{
    			double tmp = sgt1.query(1,1,n,dfn[tpn[u]],dfn[u],x);
    			ret1 = max(ret1,tmp);
    			tmp = sgt2.query(1,1,n,dfn[tpn[u]],dfn[u],x);
    			ret2 = max(ret2,tmp);
    			u = fa[tpn[u]];
    		}
    		else
    		{
    			double tmp = sgt1.query(1,1,n,dfn[tpn[v]],dfn[v],x);
    			ret1 = max(ret1,tmp);
    			tmp = sgt2.query(1,1,n,dfn[tpn[v]],dfn[v],x);
    			ret2 = max(ret2,tmp);
    			v = fa[tpn[v]];
    		}
    	}
    	if(dep[u]>dep[v]) swap(u,v);
    	double tmp = sgt1.query(1,1,n,dfn[u],dfn[v],x);
    	ret1 = max(ret1,tmp);
    	tmp = sgt2.query(1,1,n,dfn[u],dfn[v],x);
    	ret2 = max(ret2,tmp);
    	if(dcmp(ret1+ret2)>0) return true;
    	return false;
    }
    
    int main()
    {
    	scanf("%d",&n);
    	for(int i=1; i<=n; i++) scanf("%lf",&a1[i]);
    	for(int i=1; i<=n; i++) scanf("%lf",&b1[i]);
    	for(int i=1; i<=n; i++) scanf("%lf",&a2[i]);
    	for(int i=1; i<=n; i++) scanf("%lf",&b2[i]);
    	for(int i=1; i<n; i++)
    	{
    		int x,y; scanf("%d%d",&x,&y);
    		addedge(x,y); addedge(y,x);
    	}
    	dep[1] = 1; dfs1(1);
    	tpn[1] = 1; dfs2(1);
    	sgt1.build(1,1,n,a1,b1);
    	sgt2.build(1,1,n,a2,b2);
    	scanf("%d",&q);
    	for(int i=1; i<=q; i++)
    	{
    		int x,y; scanf("%d%d",&x,&y);
    		double left = 0.0,right = 1e5;
    		while(right-left>5e-5)
    		{
    			double mid = (left+right)*0.5;
    			bool ans = query(x,y,mid);
    			if(ans) {left = mid;}
    			else {right = mid;}
    		}
    		printf("%lf
    ",left);
    	}
    	return 0;
    }
    
  • 相关阅读:
    C# 打开模态对话框 和打开文件夹
    C# 统计字符串出现的个数
    html table内容不随标题滚动
    log4net 局部代码 看不懂....
    js的replace, 高亮, insertAdjacentHTML , tbody.innerHTML
    python之tkinter使用举例-Button
    使用pygal_maps_world.i18n中数据画各大洲地图
    使用pygal_maps_world展示世界地图
    python之pygal:掷两个不同的骰子并统计大小出现次数
    python之文件目录操作
  • 原文地址:https://www.cnblogs.com/suncongbo/p/11091109.html
Copyright © 2011-2022 走看看