jzoj 3196.【HNOI模拟题】物理 dp+k-d tree

8 篇文章 0 订阅

由于题目描述过于沙雕,这里给出题目大意:
n n n个人站在数轴的非负半轴上,其中一个是sg(自己理解),他站在0号点上。
对于其他的 n − 1 n-1 n1个人,第 i i i个人用三元组 ( x i , y i , l i ) (x_i,y_i,l_i) (xi,yi,li)描述,说明这个人站在 l i l_i li点上,并有一个区间 [ x i , y i ] [x_i,y_i] [xi,yi]来描述它的听觉范围。
题目给定一个 L L L,第 i i i个人能把信息传到第 j j j个人,满足 l i &gt; l j l_i&gt;l_j li>lj l i − l j &lt; = L l_i-l_j&lt;=L lilj<=L且他们的听觉范围有交,传一次信息要1秒。sg的听觉范围是 [ 0 , i n f ] [0,inf] [0,inf]。询问每个人听到信息后把信息告诉sg要多少秒。
1 ≤ n ≤ 250000 , 0 ≤ x i , y i , l i ≤ 2000000000 , 1 ≤ L ≤ 2000000000 , x i ≤ y i 1≤n≤250000,0≤x_i,y_i,l_i≤2000000000,1≤L≤2000000000,x_i≤y_i 1n2500000xi,yi,li20000000001L2000000000xiyi

分析:
我们以 l i l_i li为关键字排序, l i l_i li相同以编号排序。
因为只有右边的人可以传个左边的人,设 f [ i ] f[i] f[i]表示左数第 i i i个人传到sg需要的时间。
我们可以用一个坐标 ( x i , y i ) (x_i,y_i) (xi,yi)描述一个点,它的权值为 f [ i ] f[i] f[i]
对于 f [ j ] f[j] f[j]的转移,需要把横坐标在 [ 0 , y j ] [0,y_j] [0,yj],纵坐标在 [ x j , i n f ] [x_j,inf] [xj,inf]的点的权值取min。如果有一个点转移不到(距离超过 L L L),把这个点权值改为 i n f inf inf

代码:

#include <iostream>
#include <cstdio>
#include <algorithm>

const int maxn=250007;
const int inf=2e9;

using namespace std;

int n,L,cnt,k,root;
int f[maxn],c[maxn*2],ans[maxn];

struct data{
	int x,y,l,num;
}a[maxn];

struct rec{
    int d[2];
};

bool operator ==(rec a,rec b)
{
    return (a.d[0]==b.d[0]) && (a.d[1]==b.d[1]); 
}

struct node{
	int l,r,data;
}t[maxn*50];

bool cmp(data a,data b)
{
	if (a.l==b.l) return a.num<b.num;
	return a.l<b.l;
}

void change(int &p,rec l,rec r,rec a,int k,int op)
{
    if (!p) p=++cnt;
    if (l==r)
    {
        t[p].data=k;
        return;
    }
    int mid=(l.d[op]+r.d[op])/2;
    rec L=l,R=r;
    L.d[op]=mid+1,R.d[op]=mid;
    if (a.d[op]<=mid) change(t[p].l,l,R,a,k,op^1);
                 else change(t[p].r,L,r,a,k,op^1);
    t[p].data=inf;
    if (t[p].l) t[p].data=min(t[p].data,t[t[p].l].data); 
    if (t[p].r) t[p].data=min(t[p].data,t[t[p].r].data); 
}

void getmin(int p,rec l,rec r,rec a,rec b,int op)
{
	if (!p) return;
	if (t[p].data>=k) return;
    if ((l==a) && (r==b))
    {
    	k=min(k,t[p].data);
        return;
    }
    int mid=(l.d[op]+r.d[op])/2;
    rec L=l,R=r;
    L.d[op]=mid+1,R.d[op]=mid;
    if (b.d[op]<=mid) getmin(t[p].l,l,R,a,b,op^1);
    else if (a.d[op]>mid) getmin(t[p].r,L,r,a,b,op^1);
    else
    {
    	rec A=a,B=b;
    	A.d[op]=mid+1,B.d[op]=mid;
    	getmin(t[p].l,l,R,a,B,op^1);
    	getmin(t[p].r,L,r,A,b,op^1);
    }
}

int main()
{
	scanf("%d%d",&n,&L);
	for (int i=1;i<n;i++)
	{
		scanf("%d%d%d",&a[i].x,&a[i].y,&a[i].l);
		a[i].num=i;
	}
	a[n]=(data){0,inf,0};
	for (int i=1;i<=n;i++) 
	{
		c[i]=a[i].x,c[i+n]=a[i].y;
	}
	sort(c+1,c+n*2+1);
	int size=unique(c+1,c+2*n+1)-c-1;
    for (int i=1;i<=n;i++)
    {
    	a[i].x=lower_bound(c+1,c+size+1,a[i].x)-c;
    	a[i].y=lower_bound(c+1,c+size+1,a[i].y)-c;
	}	
	sort(a+1,a+n+1,cmp);
	int j=1;
	f[1]=0;
	change(root,(rec){{1,1}},(rec){{size,size}},(rec){{a[1].x,a[1].y}},f[1],0);			
	for (int i=2;i<=n;i++)
	{
		while (a[i].l-a[j].l>L)
		{
			change(root,(rec){{1,1}},(rec){{size,size}},(rec){{a[j].x,a[j].y}},inf,0);
			j++;
		}
		k=inf;
		getmin(root,(rec){{1,1}},(rec){{size,size}},(rec){{1,a[i].x}},(rec){{a[i].y,size}},0);
		f[i]=min(k+1,inf);
		ans[a[i].num]=f[i];
		change(root,(rec){{1,1}},(rec){{size,size}},(rec){{a[i].x,a[i].y}},f[i],0);
	}
	for (int i=1;i<n;i++) 
	{
		if (ans[i]>=inf) printf("-1\n");
		            else printf("%d\n",ans[i]);
	}
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值