@bzoj - [email protected] [POI2015] Logistyka

目录

  • @[email protected]
  • @[email protected]
  • @accepted [email protected]
  • @[email protected]

@[email protected]

维护一个长度为 n 的序列,一开始都是 0,支持以下两种操作:

1.U k a 将序列中第 k 个数修改为 a。

2.Z c s 在这个序列上,每次选出 c 个正数,并将它们都减去 1,询问能否进行 s 次操作。

每次询问独立,即每次询问不会对序列进行修改。

input

第一行包含两个正整数 n, m (1<=n, m<=1000000),分别表示序列长度和操作次数。

接下来 m 行为 m 个操作,其中 1<=k, c<=n,0<=a<=10^9,1<=s<=10^9。

output

包含若干行,对于每个 Z 询问,若可行,输出 TAK,否则输出 NIE。

sample input

3 8

U 1 5

U 2 7

Z 2 6

U 3 1

Z 2 6

U 2 2

Z 2 6

Z 2 1

sample output

NIE

TAK

NIE

TAK

@[email protected]

如果序列中的某个数 x > s,因为我们只进行 s 次 -1 的操作,这个数能用到的部分只有 s 这么多。

所以我们可以将序列中所有大于 s 的数修改成 s。

询问能够成立的必要条件是序列所有数之和 >= c*s。

而可以发现,当我们将大于 s 的数修改成 s 过后,如上的条件就变成了充要条件。

证明的话可以使用归纳法。

首先可以发现,每次操作至少保留 c 个正数。分类讨论等于 s 的数的个数:

(1)若等于 s 的数 >= c,显然成立(直接拿这几个数进行操作)。

(2)若等于 s 的数 < c,我们保证选出的 c 个数包含这些等于 s 的数。这样操作完后依然满足所有数之和 >= c*(s-1)且所有数<=(s-1),就可以归纳证明了。

至于实现,我们并不需要真的把所有大于 s 的数修改成 s,只需要统计大于 s 的数的个数再乘上 s 即可。

可以用平衡树维护,也可以用离散化 + 线段树维护。

@accepted [email protected]

#include<cstdio>
#include<iostream>
#include<algorithm>
using namespace std;
typedef long long ll;
const int MAXN = 1000000;
struct query{
    int type, x, y;
}qry[MAXN + 5];
int a[MAXN + 5], d[MAXN + 5];
int n, m, dcnt;
void discrete() {
    sort(d+1, d+dcnt+1);
    dcnt = unique(d+1, d+dcnt+1) - d - 1;
    for(int i=1;i<=m;i++)
        if( !qry[i].type )
            qry[i].y = lower_bound(d+1, d+dcnt+1, qry[i].y) - d;
}
struct node{
    int le, ri;
    ll sum; int cnt;
}tree[4*MAXN + 5];
void pushup(int x) {
    tree[x].sum = tree[x<<1].sum + tree[x<<1|1].sum;
    tree[x].cnt = tree[x<<1].cnt + tree[x<<1|1].cnt;
}
void build(int x, int l, int r) {
    tree[x].le = l, tree[x].ri = r;
    tree[x].sum = tree[x].cnt = 0;
    if( l == r ) {
        if( l == 1 ) tree[x].cnt = n;
        return ;
    }
    int mid = (l + r) >> 1;
    build(x<<1, l, mid);
    build(x<<1|1, mid+1, r);
    pushup(x);
}
void modify(int x, int p, int k) {
    if( p > tree[x].ri || p < tree[x].le )
        return ;
    if( tree[x].le == tree[x].ri ) {
        tree[x].sum += k*d[tree[x].le];
        tree[x].cnt += k;
        return ;
    }
    modify(x<<1, p, k);
    modify(x<<1|1, p, k);
    pushup(x);
}
ll s; int c;
void query(int x, int p) {
    if( d[tree[x].ri] <= p ) s += tree[x].sum;
    else if( d[tree[x].le] > p ) c += tree[x].cnt;
    else query(x<<1, p), query(x<<1|1, p);
}
char cmd[2];
int main() {
    d[dcnt = 1] = 0;
    scanf("%d%d", &n, &m);
    for(int i=1;i<=m;i++) {
        scanf("%s%d%d", cmd, &qry[i].x, &qry[i].y);
        if( cmd[0] == ‘U‘ )
            qry[i].type = 0, d[++dcnt] = qry[i].y;
        else qry[i].type = 1;
    }
    discrete(); build(1, 1, dcnt);
    for(int i=1;i<=n;i++)
        a[i] = 1;
    for(int i=1;i<=m;i++) {
        if( !qry[i].type ) {
            modify(1, a[qry[i].x], -1);
            a[qry[i].x] = qry[i].y;
            modify(1, a[qry[i].x], 1);
        }
        else {
            s = c = 0; query(1, qry[i].y);
            puts(1LL*qry[i].x*qry[i].y <= s + 1LL*c*qry[i].y ? "TAK" : "NIE");
        }
    }
}

@[email protected]

一开始找了一个比较复杂的结论,还涉及到线段树上二分等诡异的操作。

大概一两个月过后再去看……这什么玩意儿啊……我怎么看不懂我写了啥啊……

然后翻看题解才看到了这个比较简单的结论。

原文地址:https://www.cnblogs.com/Tiw-Air-OAO/p/10359090.html

时间: 2024-11-08 12:46:50

@bzoj - [email protected] [POI2015] Logistyka的相关文章

@bzoj - [email&#160;protected] [POI2015] Kinoman

目录 @[email protected] @[email protected] @accepted [email protected] @[email protected] @[email protected] 共有 m 部电影,第 i 部电影的好看值为 w[i]. 在 n 天之中每天会放映一部电影,第 i 天放映的是第 f[i] 部. 你可以选择 l, r (1 <= l <= r <= n) ,并观看第 l, l+1, -, r 天内所有的电影. 最大化观看且仅观看过一次的电影的好

@bzoj - [email&#160;protected] [POI2015] Wilcze do?y

目录 @[email protected] @[email protected] @accepted [email protected] @[email protected] @[email protected] 给定一个长度为 n 的序列,你有一次机会选中一段连续的长度不超过 d 的区间,将里面所有数字全部修改为 0. 请找到最长的一段连续区间,使得该区间内所有数字之和不超过 p . input 第一行包含三个整数 n, p, d (1 <= d <= n <= 2000000,0 &

@bzoj - [email&#160;protected] [POI2015] Pustynia

目录 @[email protected] @[email protected] @accepted [email protected] @[email protected] @[email protected] 给定一个长度为 n 的正整数序列 a,每个数都在 1 到 10^9 范围内. 告诉你其中 s 个数,并给出 m 条信息,每条信息包含三个数 l, r, k 以及 k 个正整数,表示 a[l], a[l+1], ..., a[r-1], a[r] 里这 k 个数中的任意一个都比任意一个剩

@bzoj - [email&#160;protected] [POI2015] Myjnie

目录 @[email protected] @[email protected] @accepted [email protected] @[email protected] @[email protected] 有 n 家洗车店从左往右排成一排,每家店都有一个正整数价格 p[i]. 有 m 个人要来消费,第 i 个人会驶过第 a[i] 个开始一直到第 b[i] 个洗车店,且会选择这些店中最便宜的一个进行一次消费.但是如果这个最便宜的价格大于 c[i],那么这个人就不洗车了. 请给每家店指定一个

@bzoj - [email&#160;protected] [POI2015] Odwiedziny

目录 @[email protected] @[email protected] @accepted [email protected] @[email protected] @[email protected] 给定一棵 n 个点的树,树上每条边的长度都为 1 ,第 i 个点的权值为 a[i]. Byteasar 会按照某个 1 到 n 的全排列 b 走 n-1 次,第 i 次他会从 b[i] 点走到 b[i+1] 点,并且这一次的步伐大小为 c[i]. 对于一次行走,假设起点为 x,终点为

@bzoj - [email&#160;protected] [POI2015] ?asuchy

目录 @[email protected] @[email protected] @accepted [email protected] @[email protected] @[email protected] 圆桌上摆放着 n 份食物,围成一圈,第 i 份食物所含热量为 c[i]. 相邻两份食物之间坐着一个人,共有 n 个人.每个人有两种选择,吃自己左边或者右边的食物.如果两个人选择了同一份食物,这两个人会平分这份食物,每人获得一半的热量. 假如某个人改变自己的选择后(其他 n-1 个人的选

@bzoj - [email&#160;protected] [POI2015] Wycieczki

目录 @[email protected] @[email protected] @accepted [email protected] @[email protected] @[email protected] 给定一张 n 个点 m 条边的带权有向图,每条边的边权只可能是1,2,3中的一种. 将所有可能的路径按路径长度排序,请输出第 k 小的路径的长度,注意路径不一定是简单路径,即可以重复走同一个点. input 第一行包含三个整数 n, m, k (1<=n<=40,1<=m<

@bzoj - [email&#160;protected] [Poi2011]Lightning Conductor

目录 @[email protected] @[email protected] @part - [email protected] @part - [email protected] @part - [email protected] @accepted [email protected] @version - [email protected] @version - [email protected] @[email protected] @[email protected] 已知一个长度为

@bzoj - [email&#160;protected] [POI2014]Hotel加强版

目录 @[email protected] @[email protected] @part - [email protected] @part - [email protected] @accepted [email protected] @[email protected] @[email protected] 给定一棵树,求无序三元组 (a, b, c) 的个数,使得 dis(a, b) = dis(b, c) = dis(c, a),且 a ≠ b, b ≠ c, c ≠ a. inpu