bzoj 3528: [Zjoi2014]星系调查

       这道题目,只要看懂题意,敢于展开,就是一道初中数学好题。

      首先设直线为y=kx+b,那么可知答案为Σ(kxi+b-yi)/(k^2+1),展开以后维护若干个东西就可以化简为(Ab^2+(Xk+Y)b+Uk^2+Vk+W)/(k^+1)的形式,其中A>0,那么以b为主元,最小值为(4ac-b^2)/4a,化简一下可以变成Ak^2+Bk+C/(k^2+1)的形式(A,B,C不同于之前的A,B,C),然后有两种做法,一种是变成A+(B'k+C')/(k^2+1)的形式然后双勾函数;或者设答案为t,以k为主元△>=0,然后可得到答案是一个范围,输出下界即可。

       窝用的是△法。

       于是只要各种姿势维护需要的关于xi,yi的一些和记好了。

AC代码如下:

#include<bits/stdc++.h>
#define N 100005
using namespace std;

int n,m,cnt,tot,q[N],fst[N],pnt[N<<1],nxt[N<<1],fa[N],d[N],sz[N],son[N],anc[N],id[N],rt[N];
int tx[N],ty[N]; bool vis[N];
struct node{
	int p[6];
	void ins(int u,int v){
		p[0]++; p[1]+=u; p[2]+=u*u;
		p[3]+=v; p[4]+=v*v; p[5]+=u*v;
	}
	double calc(){
		double A=p[2]-1.*p[1]*p[1]/p[0];
		double B=2*p[5]-2.*p[1]*p[3]/p[0];
		double C=p[4]-1.*p[3]*p[3]/p[0];
		double a=4,b=-4*(A+C),c=4*A*C-B*B;
		return (-b-sqrt(b*b-4*a*c))/a/2;
	}
}a[N],b[N];
node operator +(node x,node y){
	int i; for (i=0; i<6; i++) x.p[i]+=y.p[i]; return x;
}
node operator -(node x,node y){
	int i; for (i=0; i<6; i++) x.p[i]-=y.p[i]; return x;
}
void add(int x,int y){
	pnt[++tot]=y; nxt[tot]=fst[x]; fst[x]=tot;
}
void dfs(int x,int z){
	int i,y; vis[x]=1; sz[x]=1; son[x]=0; rt[x]=z;
	a[x]=a[fa[x]]; a[x].ins(tx[x],ty[x]);
	for (i=fst[x]; i; i=nxt[i]){
		y=pnt[i];
		if (!vis[y] && y!=fa[x]){
			d[y]=d[x]+1; fa[y]=x;
			dfs(y,z); sz[x]+=sz[y];
			if (sz[y]>sz[son[x]]) son[x]=y;
		}
	}
}
void dvd(int x,int tp){
	int i,y; anc[x]=tp;
	if (son[x]) dvd(son[x],tp);
	for (i=fst[x]; i; i=nxt[i]){
		y=pnt[i];
		if (x==fa[y] && y!=son[x]) dvd(y,y);
	}
}
int lca(int x,int y){
	for (; anc[x]!=anc[y]; x=fa[anc[x]])
		if (d[anc[x]]<d[anc[y]]) swap(x,y);
	return (d[x]<d[y])?x:y;
}
void cir(){
	int i,x,y; memset(vis,0,sizeof(vis));
	for (x=1; x<=n; x++)
		for (i=fst[x]; i; i=nxt[i]) if (fa[x]!=pnt[i] && fa[pnt[i]]!=x){
			y=pnt[i];
			if (d[x]>d[y]) swap(x,y);
			for (; y!=x; y=fa[y]){
				q[++cnt]=y; id[y]=cnt; vis[y]=1;
				b[cnt]=b[cnt-1]; b[cnt].ins(tx[y],ty[y]);
			}
			q[++cnt]=x; id[x]=cnt; vis[x]=1;
			b[cnt]=b[cnt-1]; b[cnt].ins(tx[x],ty[x]);
			return;
		}
}
int main(){
	scanf("%d%d",&n,&m);
	int i,x,y;
	for (i=1; i<=n; i++) scanf("%d%d",&tx[i],&ty[i]);
	for (i=1; i<=m; i++){
		scanf("%d%d",&x,&y);
		add(x,y); add(y,x);
	}
	dfs(1,1);
	memset(vis,0,sizeof(vis));
	if (n==m) cir(); else q[cnt=1]=1;
	memset(fa,0,sizeof(fa)); 
	for (i=1; i<=cnt; i++){
		dfs(q[i],q[i]); dvd(q[i],q[i]);
	}
	scanf("%d",&m);
	node u,v;
	while (m--){
		scanf("%d%d",&x,&y);
		if (rt[x]==rt[y]){
			u=a[x]+a[y]-a[lca(x,y)]-a[fa[lca(x,y)]];
			printf("%.5f\n",u.calc());
		} else{
			if (id[rt[x]]>id[rt[y]]) swap(x,y);
			u=a[x]-a[rt[x]]+a[y]-a[rt[y]]+b[id[rt[y]]]-b[id[rt[x]]-1];
			v=a[x]-a[rt[x]]+a[y]-a[rt[y]]+b[id[rt[x]]]+b[cnt]-b[id[rt[y]]-1];
			printf("%.5f\n",min(u.calc(),v.calc()));
		}
	}
	return 0;
}


by lych

2017.3.15

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值