「JOI 2016 Final」断层

嘟嘟嘟

今天我们模拟考这题,出的是T3。实在是没想出来,就搞了个20分暴力(还WA了几发)。
这题关键在于逆向思维,就是考虑最后的\(n\)的个点刚开始在哪儿,这样就减少了很多需要维护的东西。
这就让我想到很久以前的一道NOIP题,铺地毯。那是我第一次接触逆向思维,觉得十分的巧妙,原本要写的很麻烦或者干脆写不出来的题,反着想,竟然几行就完事了。
不扯别的了,还是说一下这题怎么想吧……

把操作离线,然后倒着操作,上移变成了下移。但是每一次移动两维的坐标都会改变,十分的难受。于是我们把坐标轴旋转45度,就十分美滋滋了:以顺时针举例,如果斜率为1,在新的坐标系中只有纵坐标发生了改变;斜率为-1,只有横坐标发生了改变。而且改变的这些点一定是一个前缀或者后缀。于是更新可用线段树实现。
不过更为重要的是,对于查询的\(n\)个点,无论逆向怎么操作,这些点的横、纵坐标的相对大小都不会变,大的只会更大,小的只会更小。
有了这个性质,我们就可以二分找要改的前缀(后缀)的边界了。判断的时候就是线段树单点查询。
到这里这题基本就完事了,需要注意的是,区间修改时应该加(减)的是\(2l\),因为在原本的坐标系中移动的距离是\(\sqrt{2} l\),而新坐标系的距离又是原来的\(\sqrt{2}\)倍,所以应该是\(\sqrt{2} * \sqrt{2}l\)

线段树有点慢,需要开O2才能过,改成树状数组就快很多了。

#include<cstdio>
#include<iostream>
#include<cmath>
#include<algorithm>
#include<cstring>
#include<cstdlib>
#include<cctype>
#include<stack>
#include<queue>
#include<vector>
using namespace std;
#define space putchar(' ')
#define enter puts("")
#define Mem(a, x) memset(a, x, sizeof(x))
#define In inline
typedef long long ll;
typedef double db;
const int INF = 0x3f3f3f3f;
const db eps = 1e-8;
const int maxn = 4e5 + 5;
const int N = 262144;
In ll read()
{
    ll ans = 0;
    char ch = getchar(), las = ' ';
    while(!isdigit(ch)) las = ch, ch = getchar();
    while(isdigit(ch)) ans = (ans << 1) + (ans << 3) + ch - '0', ch = getchar();
    if(las == '-') ans = -ans;
    return ans;
}
In void write(ll x)
{
    if(x < 0) putchar('-'), x = -x;
    if(x >= 10) write(x / 10);
    putchar(x % 10 + '0');
}

int n, Q;
struct Que
{
    int d; ll x, l;
}q[maxn];

struct Tree
{
    int l[maxn << 2], r[maxn << 2];
    ll lzy[maxn << 2];
    In void build(int L, int R, int now, int flg)
    {
        l[now] = L; r[now] = R;
        if(L == R) {lzy[now] = L * flg; return;}
        int mid = (L + R) >> 1;
        build(L, mid, now << 1, flg);
        build(mid + 1, R, now << 1 | 1, flg);
    }
    In void pushdown(int now)
    {
        if(lzy[now])
        {
            lzy[now << 1] += lzy[now]; lzy[now << 1 | 1] += lzy[now];
            lzy[now] = 0;
        }
    }
    In void update(int L, int R, int now, int d)
    {
        if(l[now] == L && r[now] == R) 
        {
            lzy[now] += d;
            return;
        }
        pushdown(now);
        int mid = (l[now] + r[now]) >> 1;
        if(R <= mid) update(L, R, now << 1, d);
        else if(L > mid) update(L, R, now << 1 | 1, d);
        else update(L, mid, now << 1, d), update(mid + 1, R, now << 1 | 1, d);
    }
    In ll query(int now, int id)
    {
        if(l[now] == r[now]) return lzy[now];
        pushdown(now);
        int mid = (l[now] + r[now]) >> 1;
        if(id <= mid) return query(now << 1, id);
        else return query(now << 1 | 1, id);
    }
}t1, t2;

int main()
{
    n = read(), Q = read();
    for(int i = Q; i; --i) q[i].x = read(), q[i].d = read(), q[i].l = read();
    t1.build(0, N - 1, 1, 1), t2.build(0, N - 1, 1, -1);
    for(int i = 1; i <= Q; ++i)
    {
        if(q[i].d == 1)
        {
            int L = 0, R = N - 1;
            while(L < R)
            {
                int mid = ((L + R) >> 1) + 1;
                if(t2.query(1, mid) < -q[i].x) R = mid - 1;
                else L = mid;
            }
            t1.update(0, L, 1, -q[i].l * 2);
        }
        else
        {
            int L = 0, R = N - 1;
            while(L < R)
            {
                int mid = ((L + R) >> 1) + 1;
                if(t1.query(1, mid) > q[i].x) R = mid - 1;
                else L = mid;
            }
            if(L + 1 > N - 1) --L;
            t2.update(L + 1, N - 1, 1, -q[i].l * 2);
        }
    }
    for(int i = 1; i <= n; ++i) write(-(t2.query(1, i) + t1.query(1, i)) / 2), enter;
    return 0;
}

转载于:https://www.cnblogs.com/mrclr/p/10438820.html

Joi 是一个用于 JavaScript 的强大的对象模型验证库。它可以用来验证和转换复杂的数据结构,如表单输入、API 请求和配置文件。Joi 提供了一组强大的验证规则和函数,可以轻松定义和应用对输入数据的验证逻辑。 Joi 的主要特点包括: 1. 可以通过链式调用来定义验证规则,使代码更加清晰和易读。 2. 支持各种类型的验证,包括字符串、数字、日期、枚举、数组、对象等。 3. 支持自定义验证规则和错误消息。 4. 提供丰富的验证函数,如必需字段、字符串长度、正则表达式匹配、数值范围、枚举值等。 5. 支持异步验证和自动转换。 6. 可以通过 `.validate()` 方法对数据进行验证,并返回验证结果。 以下是一个使用 Joi 进行表单验证的示例: ```javascript const Joi = require('joi'); // 定义验证规则 const schema = Joi.object({ username: Joi.string().alphanum().min(3).max(30).required(), password: Joi.string().pattern(new RegExp('^[a-zA-Z0-9]{3,30}$')).required(), email: Joi.string().email({ minDomainSegments: 2, tlds: { allow: ['com', 'net'] } }).required(), age: Joi.number().integer().min(18).max(99).required(), }); // 准备待验证的数据 const data = { username: 'john123', password: 'Password123', email: 'john@example.com', age: 25, }; // 进行验证 const result = schema.validate(data); // 输出验证结果 if (result.error) { console.log(result.error.details); } else { console.log('Validation passed'); } ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值