【bzoj3641】货车运输

11 篇文章 0 订阅
11 篇文章 0 订阅

此题就是在基环树上的询问

答案分为两部分,一部分是以询问限速开过的时间,一部分是以当前路的限速开过的段。

考虑离线,每次讲限速小于当前询问的限速的路段权值修改。

S->T如果在去环后在同一棵树上,就直接树链+树状数组。

如果不在同一棵树上就取他们到根路径的权值和和环上两种走法的较小值作为答案。

环上边另开树状数组即可

(claris只去一条边变为一棵树的太神了)

#include <bits/stdc++.h>
#define gc getchar()
#define N 100009
using namespace std;
int n,m,qq,first[N],number=1,v[N],w[N],vis[N],pre[N],root[N],so[N],sw[N];
int root_num,rt[N],fa[N],size[N],Mson[N],deep[N],top[N],cnt,dfn[N],id[N];
int is_root[N],limit[4];
double Ans[N],bit[N<<1][4];
map<int,int> mp;
struct edge
{
	int to,next,len,pd,pos,lev;
	double val,v;
	void add(int x,int y,int z,int l,int p)
	{
		to=y,next=first[x],first[x]=number,lev=z,len=l,pos=p;
	}
}e[N<<1];
struct Qry
{
	int x,y,pos;
	double v;
	bool operator <(const Qry &rhs) const
	{
		return v<rhs.v;
	}
}q[N];
bool cmp(const int &x,const int &y)
{
	return e[x].v<e[y].v;
}
int read()
{
	int x=1;
	char ch;
	while (ch=gc,ch<'0'||ch>'9') if (ch=='-') x=-1;
	int s=ch-'0';
	while (ch=gc,ch<='9'&&ch>='0') s=s*10+ch-'0';
	return s*x;
}
int lowbit(int x)
{
	return x&(-x);
}
void add(int x,double y,int k)
{
	for (;x<=limit[k];x+=lowbit(x)) bit[x][k]+=y;
}
double qry(int x,int k=0,double ret=0.0)
{
	for (;x;x-=lowbit(x)) ret+=bit[x][k];
	return ret;
}
double qry(int x,int y,int k)
{
	return qry(y,k)-qry(x-1,k);
}
void dfs(int x,int last)
{
	vis[x]=1;
	for (int i=first[x];i;i=e[i].next)
		if (vis[e[i].to]&&i!=(last^1))
		{
			int now=x;
			while (now!=e[i].to)
			{
				e[pre[now]].pd=e[pre[now]^1].pd=1;
				mp[pre[now]]=mp[pre[now]^1]=now;
				root[++root_num]=now;
				now=e[pre[now]^1].to;
			}
			e[i].pd=e[i^1].pd=1;
			mp[i]=mp[i^1]=e[i].to;
			root[++root_num]=e[i].to;
			pre[e[i].to]=i;
			break;
		}
		else
			if (!vis[e[i].to]&&i!=(last^1))
			{
				pre[e[i].to]=i;
				dfs(e[i].to,i);
				if (root_num) break;
			}
}
void dfs1(int x,int r)
{
	rt[x]=r;
	size[x]=1;
	deep[x]=deep[fa[x]]+1;
	for (int i=first[x];i;i=e[i].next)
		if (!e[i].pd&&e[i].to!=fa[x])
		{
			pre[e[i].to]=i;
			mp[i]=mp[i^1]=e[i].to;
			fa[e[i].to]=x;
			dfs1(e[i].to,r);
			size[x]+=size[e[i].to];
			if (size[e[i].to]>size[Mson[x]]) Mson[x]=e[i].to;
		}
}
void dfs2(int x,int y)
{
	id[dfn[x]=++cnt]=x;
	top[x]=y;
	if (Mson[x]) dfs2(Mson[x],y);
	for (int i=first[x];i;i=e[i].next)
		if (e[i].to!=fa[x]&&e[i].to!=Mson[x]&&!e[i].pd) dfs2(e[i].to,e[i].to);
}
double query(int x,int y,double z,double ret=0.0)
{
	for (;top[x]!=top[y];x=fa[top[x]])
	{
		if (deep[top[x]]<deep[top[y]]) swap(x,y);
		ret+=qry(dfn[top[x]],dfn[x],0)+qry(dfn[top[x]],dfn[x],1)/z;
	}
	if (deep[x]>deep[y]) swap(x,y);
	ret+=qry(dfn[x]+1,dfn[y],0)+qry(dfn[x]+1,dfn[y],1)/z;
	return ret;
}
int main()
{
	freopen("1.in","r",stdin);
	freopen("1.out","w",stdout);
	n=read(),m=read(),qq=read();
	for (int i=1;i<=n;i++)
	{
		int x=read(),y=read(),l=read(),z=read();
		e[++number].add(x,y,z,l,i);
		e[++number].add(y,x,z,l,i);
	}
	for (int i=1;i<=m;i++) v[i]=read(),w[i]=read();
	for (int i=1;i<=n;i++)
	{
		int level=e[i<<1].lev;
		e[i<<1].v=e[i<<1|1].v=v[level];
		e[i<<1].val=e[i<<1|1].val=(double)e[i<<1].len*w[level];
	}
	dfs(1,0);
	for (int i=1;i<=root_num;i++)
		sw[i]=root[root_num-i+1];
	for (int i=1;i<=root_num;i++)
	{
		root[i]=sw[i];
		dfs1(root[i],root[i]),dfs2(root[i],root[i]);
		is_root[root[i]]=i;
	}
	for (int i=1;i<=n;i++) so[i]=i<<1;
	sort(so+1,so+n+1,cmp);
	for (int i=1;i<=qq;i++)
		q[i].x=read(),q[i].y=read(),q[i].v=read(),q[i].pos=i;
	limit[0]=limit[1]=n,limit[2]=limit[3]=(root_num<<1);
	for (int i=1;i<=(root_num<<1);i++)
		add(i,e[pre[root[(i-1)%root_num+1]]].val,3);
	sort(q+1,q+qq+1);
	for (int i=1;i<=n;i++)
		if (!is_root[i]) add(dfn[i],e[pre[i]].val,1);
	int now_edge=1;
	for (int i=1;i<=qq;i++)
	{
		while (now_edge<=n&&e[so[now_edge]].v<=q[i].v)
		{
			int now=so[now_edge];
			if (e[now].pd)
			{
				//cout<<"updata edge:"<<e[now].to<<" "<<e[now^1].to<<endl;
				add(is_root[mp[now]],e[now].val/e[now].v,2);
				add(is_root[mp[now]],-e[now].val,3);
				add(is_root[mp[now]]+root_num,e[now].val/e[now].v,2);
				add(is_root[mp[now]]+root_num,-e[now].val,3);
			}
			else
			{
				add(dfn[mp[now]],e[now].val/e[now].v,0);
				add(dfn[mp[now]],-e[now].val,1);
			}
			now_edge++;
		}
		int r1=rt[q[i].x],r2=rt[q[i].y];
		if (r1==r2) Ans[q[i].pos]=query(q[i].x,q[i].y,q[i].v);
		else
		{
			Ans[q[i].pos]=query(q[i].x,r1,q[i].v)+query(q[i].y,r2,q[i].v);
			int R1=is_root[r1],R2=is_root[r2];
			if (R1>R2) swap(R1,R2);
			double val1=qry(R1+1,R2,2)+qry(R1+1,R2,3)/q[i].v;
			double val2=qry(R2+1,R1+root_num,2);
			val2+=qry(R2+1,R1+root_num,3)/q[i].v;
			Ans[q[i].pos]+=min(val1,val2);
		}
	}
	for (int i=1;i<=qq;i++)
		printf("%lf\n",Ans[i]);
	return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值