【JZOJ】3196 物理

Description

sg站在 0 位置,后面有n1个人,第 i 个人在位置Pi能听到(发出)的声音频率范围为 [Xi,Yi] ,sg能听到(发出)所有声音。声音传播有一个距离D,第i个人的声音最多只能传到 PiD ,只能朝着sg的方向传播。声音在两个人之间传播的时间均为 1 。问:第i个人发出声音,能否传到sg处,若能,输出最短时间,若不能,输出 1 .注意:声音的频率在传话的过程中可以改变,改变只取决于当前说话的人的频率范围。
空间限制 1G , 时间限制 5s

TuCao

这已经是非常好的题意了。。。。原题的信息更加confusing。例如只能朝一个方向传播题解和样例都没解释,声音频率能改也没有特别说明,导致我认为这题无比的复杂。

Preliminary Analysis

由于声音频率是可以改变的。
某个人传话给sg和sg传话他所花的时间可途径都是一样的,把对象反一下就好了。
我们只需要求sg传话给每个人的时间就好。
我们设 Fi 表示sg传话给第i个人所需要的时间。
我们不难想到一个DP式

Fi=min(Fj)+1,PiDPjPi and [Xj,Yj][Xi,Yj]

好的看懂题目这样的DP式就能有50分了= =
然而现场所有人都打了DFS因为以为可以往回走。

Solution

根据这个DP式我们可以知道对于位置 i 我们需要什么:
1.合法的位置Pj
2.合法的 [Xj,Yj]

我们对于 Pi 排个序,那么可选的决策就是一段连续的区间。
那么怎么知道 [Xj,Yj] 合不合法呢?
我们不需要知道。
按照权值建一个线段树,每个决策 [Xj,Yj] 附上该决策值。只需要知道 [Xi,Yi] 内的最小值就可以了。

这样非常暴力,鉴于1G的空间我们不理了。
因为决策区间会移动,所以我们有必要删除队首元素。删除,也就是撤销,是普通线段树无法实现的功能。可持久化?或许可以?应该是不行的空间太大1G都不够用。
没错就是单调队列。
线段树套单调队列。
我们对于线段树的每个区间都开一个单调队列来维护一个该区间内单调递增的决策,一旦遇到位置不合法就弹出队首即可。每次插入都是 logn 的,只会插入 logn 个点。
还有一个问题:
我们查询的区间不一定和之前完全相同,有交集就行。
如果我们只对线段树普通的赋值,完全覆盖才加入,这样就会丢失很多信息,简而言之就是错的。
我们对每个区间维护两个单调队列,Cover和Pass
Cover:完全覆盖该区间的决策的单调队列
Pass:经过该区间的决策的单调队列

也可以理解为
Cover:当前点的信息
Pass:该点及其子树的信息

这样我们查询的时候,除了查询是所有经过的区间的Cover,对于查询区间所完全覆盖的区间,我们把它们的Pass也算上。
这样一来,我们就可以得出正确答案了。

PS.数字范围很大请务必离散化
离散化能不用Hash就别用吧
一个区间在修改时,能更新Cover就没必要更新Pass了,因为Cover Pass

常数较大,祝君好运。
此题的思想可以灵活运用于二维矩形赋值,关于维护二维的线段树等等。

#include<cstring>
#include<cstdio>
#include<algorithm>
#include<vector>
#include<map>
using namespace std;

const int N = 250010;

map<int , int> Hash;

struct Que{
    int head,tail;
    vector<int> q;
};

struct Node{
    int l,r;
    Que Cover , Pass;
}tree[N * 20];

struct Data{
    int x,y,pos;
}a[N];

int F[N],n,l,mxt,myt,tot,inf,c[N * 2],P,Pos;

void Relable()
{
    int td = 0 , id = 0;
    for (int i = 1 ; i <= n ; i ++) c[++ td] = a[i].x , c[++ td] = a[i].y;
    sort(c + 1 , c + 1 + td);
    for (int i = 1 ; i <= td ; i ++) if (!Hash[c[i]]) Hash[c[i]] = ++ id ;
    for (int i = 1 ; i <= n ; i ++) a[i].x = Hash[a[i].x] , a[i].y = Hash[a[i].y];
    mxt = 1 , myt = id;
}

void Update(Que &t)
{
    while (t.tail >= t.head && F[t.q[t.tail]] >= F[P]) t.tail --;
    ++ t.tail;
    if (t.tail < t.q.size()) t.q[t.tail] = P; else t.q.push_back(P);
}

void Mark(int now , int L , int R , int l , int r)
{
    if (L == l && r == R)
    {
        Update(tree[now].Cover);
    //  Update(tree[now].Pass , p);
        return;
    }

    Update(tree[now].Pass);
    int mid = (L + (long long)R) >> 1;
    if (r <= mid)
    {
        if (!tree[now].l)
            tree[now].l = ++ tot , tree[tot].Cover.tail = tree[tot].Pass.tail = -1;
        Mark(tree[now].l , L , mid , l , r); return ;
    }
    if (l > mid)
    {
        if (!tree[now].r)
            tree[now].r = ++ tot , tree[tot].Cover.tail = tree[tot].Pass.tail = -1;
        Mark(tree[now].r , mid + 1 , R , l , r); return ;
    }

    if (!tree[now].l)
        tree[now].l = ++ tot , tree[tot].Cover.tail = tree[tot].Pass.tail = -1;
    if (!tree[now].r)
        tree[now].r = ++ tot , tree[tot].Cover.tail = tree[tot].Pass.tail = -1;

    Mark(tree[now].l , L , mid , l , mid);
    Mark(tree[now].r , mid + 1 , R , mid + 1 , r);
    return;
}

inline int Gethead(Que &t)
{
    while (t.head <= t.tail && a[t.q[t.head]].pos < Pos) t.head ++;
    if (t.head > t.tail) return inf;
    return F[t.q[t.head]];
}

int Query(int now , int L , int R , int l , int r)
{
    int mid = (L + (long long)R) >> 1 , flag = inf;
/*  Gethead(tree[now].Cover);
    if (tree[now].Cover.head <= tree[now].Cover.tail)
            flag = F[tree[now].Cover.q[tree[now].Cover.head]];*/
    flag = Gethead(tree[now].Cover);
    if (L == l && R == r)
    {
        /*Gethead(tree[now].Pass);
        if (tree[now].Pass.head <= tree[now].Pass.tail)
            flag = min(flag , F[tree[now].Pass.q[tree[now].Pass.head]]);*/
        flag = min(flag , Gethead(tree[now].Pass));
        return flag;
    }

    if (r <= mid)
    {
        if (!tree[now].l) return flag;
        return min(flag , Query(tree[now].l , L , mid , l , r));
    }
    if (l > mid)
    {
        if (!tree[now].r) return flag;
        return min(flag , Query(tree[now].r , mid + 1 , R , l , r));
    }

    if (tree[now].l) flag = min(flag , Query(tree[now].l , L , mid , l , mid));
    if (tree[now].r) flag = min(flag , Query(tree[now].r , mid + 1 , R , mid + 1 , r));
    return flag;
}

int main()
{
    freopen("2.in" , "r" , stdin);freopen("2.out" , "w" , stdout);
    scanf("%d%d" , &n , &l);

    mxt = 2000000010;
    tot = 1 , inf = N * 2;
    for (int i = 2 ; i <= n ; i ++)
    {
        scanf("%d%d%d" , &a[i].x , &a[i].y , &a[i].pos);
        mxt = min(a[i].x , mxt);
        myt = max(a[i].y , myt);
    }
    a[1].x = mxt , a[1].y = myt , a[1].pos = 0;
    Relable();

    tree[1].Cover.tail = tree[1].Pass.tail = -1;
    P = 1 , Pos = 0;
    Mark(1 , mxt , myt , a[1].x , a[1].y);
    for (int i = 2 ; i <= n ; i ++)
    {
        Pos = a[i].pos - l;
        if (a[i].pos >= l) F[i] = Query(1 , mxt , myt , a[i].x , a[i].y); else F[i] = 0;
        //F[i] = Query(1 , mxt , myt , a[i].x , a[i].y);
        if (F[i] == inf) {F[i] = -1 ; continue;}
        F[i] ++;P = i;
        Mark(1 , mxt , myt , a[i].x , a[i].y);
    }

    for (int i = 2 ; i <= n ; i ++) printf("%d\n" , F[i]);
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值