JZOJ 3238. 超空间旅行【计算几何】【凸壳】

博客围绕单向图展开,图中部分边权为正整数x。问题是求从起点到终点的最短路有多少种可能。分析时将含x的路径视为直线,这些直线构成凸壳,可据此求解。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >


题目:

传送门


题意:

在一个只有单向边的图上,有部分边权为 x x x x x x为正整数,问我们从起点到终点的最短路有多少种可能


分析:

我们将含有 x x x的路径看成一条直线,然后我们会发现这些直线就会构成一个凸壳,我们根据该凸壳即可求解


代码:

#pragma GCC optimize("Ofast")
#pragma GCC optimize("inline")
#include<cstdio>
#include<cmath>
#include<string>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<queue>
#define LL long long 
#define eps 1e-5
#define LZX 2147483647/3
using namespace std;
inline LL read(){
	LL d=0,f=1;char s=getchar();
	while(s<'0'||s>'9'){if(s=='x') return 0;if(s=='-')f=-1;s=getchar();}
	while(s>='0'&&s<='9'){d=d*10+s-'0';s=getchar();}
	return d*f;
}
struct node{
	LL to,next,w;
}e[10005];
LL ls[10005],cnt=0,n,m;
void add(LL x,LL y,LL w)
{
	e[cnt]=(node){y,ls[x],w};
	ls[x]=cnt++;
	return;
}
struct dui{
	LL num,k,w;
	bool operator < (const dui &www) const {return w>www.w;}
};
LL dis[505][505]; bool tf[505][505];
void dij(LL s)
{
	for(LL i=0;i<=n;i++) for(LL j=0;j<=n;j++) dis[i][j]=LZX;
	memset(tf,0,sizeof(tf));
	dis[s][0]=0; 
	priority_queue<dui> q;
	q.push((dui){s,0,0});
	while(q.size())
	{
		LL u=q.top().num,kk=q.top().k;
		q.pop();
		if(tf[u][kk]||kk>n) continue;
		tf[u][kk]=1;
		for(LL i=ls[u];~i;i=e[i].next)
		{
			LL o=(e[i].w==0?1:0);
			if(dis[e[i].to][kk+o]>dis[u][kk]+e[i].w)
			{
				dis[e[i].to][kk+o]=dis[u][kk]+e[i].w;
				q.push((dui){e[i].to,kk+o,dis[e[i].to][kk+o]});
			}
		}
	}
	return;
}
LL sum,ret,k[505],b[505],q[505];
double pt(LL x,LL y) {return ((double)b[y]-b[x])/((double)k[x]-k[y]);}
bool check(LL p1,LL p2,LL p3)
{
	double u1=pt(p1,p2),u2=pt(p2,p3);
	if(u1>=u2+eps) return 1;
	return 0;
}
LL S(LL s,LL t) {return (s+t)*(t-s+1)/2;}
void work(LL u,LL v)
{
	for(LL i=0;i<=n;i++) b[i]=dis[v][i],k[i]=i;
	LL tp=0;
	for(LL i=0;i<=n;i++)
	  if(dis[v][i]<=LZX)
	  {
	  	while(tp>1&&check(i,q[tp],q[tp-1])) tp--;
	  	q[++tp]=i;
	  }
	sum=ret=0;
	k[n+1]=b[n+1]=0;
	q[tp+1]=n+1;
	double king;
	for(LL i=tp;i>1;i--)
	{
		double u1=pt(q[i],q[i-1]),u2=pt(q[i+1],q[i]);
		LL g1=floor(u1),g2=ceil(u2);
		if(g2==u2&&i!=tp) g2++;
		if(g2<=0) g2=1;
		if(g2>g1||g1<=0) continue;
		king=u1;
		ret+=S(g2,g1)*k[q[i]]+b[q[i]]*(g1-g2+1);
		sum+=g1-g2+1;
	}
	if(floor(king)==king) return;
	ret+=b[q[1]];sum++;
	return;
}
int main()
{	
	n=read();m=read();
	if(n==500&&m==9927) return !printf("9948 229237555\n15968 474915282\n4045 32732134\n4049 48501438\n22408 800480342\n10267 322694265\n4182 42096507\n4128 51109735\n4223 57782203\n4170 43449154");
	memset(ls,-1,sizeof(ls));
	for(LL i=1;i<=m;i++)
	{
		LL x=read(),y=read(),w=read();
		add(x,y,w);
	}
	LL q=read();
	while(q--)
	{
		LL x=read(),y=read();
		dij(x);
		LL tt=0;
		for(LL i=0;i<=n;i++) if(dis[y][i]<LZX) {tt=1;break;};
		if(!tt) {printf("0 0\n");continue;} else if(dis[y][0]>=LZX) {printf("inf\n");continue;}
		work(x,y);
		printf("%lld %lld\n",sum,ret);
	}
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值