@NOI模拟2017.07.02 - T1@ Attack
@description@
『新的风暴已经出现,怎么能够停滞不前』——你决定去攻击小怪兽的巢⽳。
怪兽有⼀⾏ n 个巢⽳,从 1 到 n 编号,第 i 个巢⽳的防御⼒为 Ri。
⼀开始你在降⽣在第 x 个巢⽳(此时巢⽳ x 已被破坏),攻击⼒为 Rx。
每次你有三种操作:
- 攻击你左边的第⼀个没有被摧毁的巢⽳,要求你的攻击⼒要⼤于等于它的防御⼒。
- 攻击你右边的第⼀个没有被摧毁的巢⽳,要求你的攻击⼒要⼤于等于它的防御⼒。
- 增加你的攻击⼒,这会占用你 K 次操作,你的攻击⼒会变成两边第⼀个没有被摧毁的巢⽳防御⼒的较小值(不存在算作 inf)。
令 Ex 等于你出⽣在 x 的时候,捣毁所有巢⽳需要的最少次数。
现在有 Q 个询问,每次有两种操作:
- 交换巢⽳ x 和 x + 1。
- 给出两个数字 x 和 y,求 \(\sum^{y}_{i=x} E_i\) 的值。
input
第⼀⾏两个整数 n 和 k。
第⼆⾏ n 个整数,表示 Ri。
之后若⼲⾏(q ⾏),开始⼀个数 op 表示操作类型,如果 op = 1,接下来⼀个数 x。
否则 op = 2,接下来两个数字 x 和 y。
output
每⾏⼀个整数表示答案。
sample input
5 3
2 3 1 4 1
2 2 2
2 1 5
1 2
2 2 2
2 1 5
sample output
7
38
13
41
100% 的分数满⾜ n ≤ 10^5, k ≤ 10^6, Ri ≤ 10^9, q ≤ 2 ∗ 10^5, x < n。
@solution@
考虑对于一个给定的 x,怎么求解 Ex。设改变了 c 次攻击力,则显然 Ex = c*k + (n-1)。
于是问题转变为对于给定的 x,改变了多少次攻击力。
一种想法是通过递推转移。令 f[x] 表示从 x 开始改变的攻击力次数。
找到 x 左边第一个比 Rx 大的数的位置 lm,右边第一个比它 Rx 大的数的位置 rm,则设 p 为 lm 与 rm 之中对应着较小的数的位置。
于是有着 f[x] = f[p] + 1。
另一种想法是考虑一个数对另一个数的贡献。考虑 x,找到 x 右边第一个大于或等于它的数 y。
则对于 x 与 y 之间的数,它们肯定要在 x 这个地方改变一次攻击力。所以 x 对于 x 与 y 之间的数产生贡献。
但是注意假如 Rx = Ry 的时候,按照上述计算方法是会在 Rx 算了一次,Ry 又重复算了一次。但其实 Rx 与 Ry 是在同一次改变攻击力。所以要特判。
考虑修改时怎么维护。假如交换 x 与 x+1,不妨令 R[x] > R[x+1](反过来同理)。
可以发现对于 x 基本什么不会都不会变。对于 x+1,我们先通过递推找到它新的 Ex。然后通过线段树对它所影响到的区间进行修改。
同时我们也可以通过线段树上二分 O(log^2) 求出 x 之前/之后最靠近 x 的比 Rx 大的数。
具体操作怎么实现的可以参考代码。
一个小细节:假如完全按照上面实现,你需要在线段树上既实现找最靠近 x 大于 Rx 又要实现找最靠近 x 大于等于 Rx。
对于等于的情况,我们不妨维护一个链表,将 x 前/后最靠近的等于 Rx 的连接起来,在交换的时候简单维护一下即可。
@accepted code@
#include<cstdio>
#include<algorithm>
using namespace std;
typedef long long ll;
const int MAXN = 100000;
int R[MAXN + 5];
struct segtree {
struct node{
int mx, le, ri;
int tag; ll sum;
}t[4*MAXN + 5];
void pushup(int x) {
t[x].mx = (R[t[x<<1].mx] > R[t[x<<1|1].mx]) ? t[x<<1].mx : t[x<<1|1].mx;
t[x].sum = t[x<<1].sum + t[x<<1|1].sum;
}
void build(int x, int l, int r) {
t[x].le = l, t[x].ri = r, t[x].tag = t[x].sum = 0;
if( l == r ) {
t[x].mx = l;
return ;
}
int mid = (l + r) >> 1;
build(x<<1, l, mid);
build(x<<1|1, mid+1, r);
pushup(x);
}
void pushdown(int x) {
if( t[x].tag ) {
t[x<<1].tag += t[x].tag, t[x<<1].sum += t[x].tag*(t[x<<1].ri-t[x<<1].le+1);
t[x<<1|1].tag += t[x].tag, t[x<<1|1].sum += t[x].tag*(t[x<<1|1].ri-t[x<<1|1].le+1);
t[x].tag = 0;
}
}
void add(int x, int l, int r, int d) {
if( l > t[x].ri || r < t[x].le )
return ;
if( l <= t[x].le && t[x].ri <= r ) {
t[x].tag += d;
t[x].sum += d*(t[x].ri-t[x].le+1);
return ;
}
pushdown(x);
add(x<<1, l, r, d), add(x<<1|1, l, r, d);
pushup(x);
}
void update(int x, int p) {
if( p > t[x].ri || p < t[x].le )
return ;
if( t[x].le == t[x].ri )
return ;
pushdown(x);
update(x<<1, p), update(x<<1|1, p);
pushup(x);
}
int query_mx(int x, int p, int type) {
if( t[x].le == t[x].ri )
return (R[t[x].le] > R[p]) ? t[x].le : -1;
pushdown(x);
if( type ) {
if( p >= t[x<<1].ri )
return query_mx(x<<1|1, p, type);
else if( p < t[x<<1].le ) {
if( R[t[x].mx] <= R[p] ) return -1;
else return (R[t[x<<1].mx] > R[p]) ? query_mx(x<<1, p, type) : query_mx(x<<1|1, p, type);
}
else {
int q = query_mx(x<<1, p, type);
if( q == -1 ) return query_mx(x<<1|1, p, type);
else return q;
}
}
else {
if( p <= t[x<<1|1].le )
return query_mx(x<<1, p, type);
else if( p > t[x<<1|1].ri ) {
if( R[t[x].mx] <= R[p] ) return -1;
else return (R[t[x<<1|1].mx] > R[p]) ? query_mx(x<<1|1, p, type) : query_mx(x<<1, p, type);
}
else {
int q = query_mx(x<<1|1, p, type);
if( q == -1 ) return query_mx(x<<1, p, type);
else return q;
}
}
}
int query_sum(int x, int l, int r) {
if( l > t[x].ri || r < t[x].le )
return 0;
if( l <= t[x].le && t[x].ri <= r )
return t[x].sum;
pushdown(x);
return query_sum(x<<1, l, r) + query_sum(x<<1|1, l, r);
}
}T;
int d[MAXN + 5], lst[MAXN + 5], nxt[MAXN + 5], adj[MAXN + 5];
int main() {
freopen("attack.in", "r", stdin);
freopen("attack.out", "w", stdout);
int n, k; scanf("%d%d", &n, &k);
for(int i=1;i<=n;i++)
scanf("%d", &R[i]), d[i] = R[i];
sort(d + 1, d + n + 1);
int dsiz = unique(d + 1, d + n + 1) - d - 1;
for(int i=1;i<=n;i++)
R[i] = lower_bound(d + 1, d + dsiz + 1, R[i]) - d;
R[0] = R[n+1] = dsiz + 1;
for(int i=1;i<=dsiz;i++)
adj[i] = n + 1;
for(int i=n;i>=1;i--) {
nxt[i] = adj[R[i]];
lst[adj[R[i]]] = i;
adj[R[i]] = i;
}
for(int i=1;i<=dsiz;i++)
lst[adj[i]] = 0;
T.build(1, 0, n+1);
for(int i=1;i<=n;i++) {
int x = T.query_mx(1, i, 0), y = T.query_mx(1, i, 1);
if( x >= lst[i] )
T.add(1, x + 1, i - 1, 1);
if( y <= nxt[i] )
T.add(1, i + 1, y - 1, 1);
else T.add(1, i + 1, nxt[i] - 1, 1);
}
int op;
while( scanf("%d", &op) == 1 ) {
if( op == 2 ) {
int l, r; scanf("%d%d", &l, &r);
printf("%lld\n", 1LL*k*T.query_sum(1, l, r) + 1LL*(n-1)*(r-l+1));
}
else {
int x, y; scanf("%d", &x); y = x + 1;
if( R[x] == R[y] ) continue;
if( R[x] > R[y] ) {
int p = T.query_mx(1, y, 1);
if( p <= nxt[y] )
T.add(1, y + 1, p - 1, -1);
int xs = T.query_sum(1, x, x), ys = T.query_sum(1, y, y);
T.add(1, x, x, -xs), T.add(1, y, y, xs-ys);
swap(R[x], R[y]), T.update(1, x), T.update(1, y);
swap(nxt[x], nxt[y]), swap(lst[x], lst[y]);
lst[nxt[x]] = nxt[lst[x]] = x;
lst[nxt[y]] = nxt[lst[y]] = y;
int q = T.query_mx(1, x, 0);
T.add(1, x, x, R[q] > R[y] ? T.query_sum(1, y, y) + 1 : T.query_sum(1, q, q) + 1);
if( q >= lst[x] )
T.add(1, q + 1, x - 1, 1);
}
else {
int p = T.query_mx(1, x, 0);
if( p >= lst[x] )
T.add(1, p + 1, x - 1, -1);
int xs = T.query_sum(1, x, x), ys = T.query_sum(1, y, y);
T.add(1, x, x, ys-xs), T.add(1, y, y, -ys);
swap(R[x], R[y]), T.update(1, x), T.update(1, y);
swap(nxt[x], nxt[y]), swap(lst[x], lst[y]);
lst[nxt[x]] = nxt[lst[x]] = x;
lst[nxt[y]] = nxt[lst[y]] = y;
int q = T.query_mx(1, y, 1);
T.add(1, y, y, R[q] > R[x] ? T.query_sum(1, x, x) + 1 : T.query_sum(1, q, q) + 1);
if( q <= nxt[y] )
T.add(1, y + 1, q - 1, 1);
}
}
}
}
@details@
一道思维量适中,应用线段树的,算是比较典型的题吧。
只是对于大于小于、等于等情况讨论有些令人头疼。
@NOI模拟2017.07.02 - T1@ Attack的更多相关文章
- @NOI模拟2017.06.30 - T1@ Left
目录 @description@ @solution@ @accepted code@ @details@ @description@ JOHNKRAM 最近在研究排序网络,但他发现他不会制作比较器, ...
- @NOI模拟2017.06.30 - T3@ Right
目录 @description@ @solution@ @part - 1@ @part - 2@ @accepted code@ @details@ @description@ JOHNKRAM 和 ...
- 7.11 NOI模拟赛 qiqi20021026的T1 四个指针莫队 trie树
LINK:qiqi20021026的T1 考场上只拿到了50分的\(nq\)暴力. 考虑一个区间和一个区间配对怎么做 二分图最大带权匹配复杂度太高. 先考虑LCS的问题 常见解决方法是后缀数组/tri ...
- 5.30 NOI 模拟
$5.30\ NOI $模拟 高三大哥最后一次模拟考了,祝他们好运 \(T1\)装箱游戏 显然可以将四种字母之间的空缺当做状态枚举 那么这道题就很显然了 #include<bits/stdc++ ...
- 5.23 NOI 模拟
$5.23\ NOI $模拟 \(T1\)简单的计算几何题 \(zjr:\)我当时没改,那么自己看题解吧 倒是有个简单的随机化方法(能获得\(72pts,\)正确性未知)\(:\) 随机两条切椭圆的平 ...
- 5.6 NOI模拟
\(5.6\ NOI\)模拟 明天就母亲节了,给家里打了个电话(\(lj\ hsez\)断我电话的电,在宿舍打不了,只能用教练手机打了) 其实我不是很能看到自己的\(future,\)甚至看不到高三的 ...
- 5.4 NOI模拟
\(5.4\ NOI\)模拟 \(T1\) 想到分讨,但是暴力输出一下方案之后有很多特别的情况要讨论,就弃了... 假设\(a\)是原序列,\(b\)是我们得到的序列 设\(i\)是最长公共前缀,\( ...
- 日本IT行业劳动力缺口达22万 在日中国留学生迎来就业好时机 2017/07/18 11:25:09
作者:倪亚敏 来源:日本新华侨报 发布时间:2017/07/18 11:25:09 据日本政府提供的数据,日本2018年应届毕业生的“求人倍率”已经达到了1.78倍.换言之,就是100名大学生 ...
- hdu6034[模拟] 2017多校1
/*hdu6034[模拟] 2017多校1*/ //暴力模拟26个26进制数即可, 要注意进位 #include<bits/stdc++.h> using namespace std; t ...
随机推荐
- flask中cookie和session设置
flask中cookie和session介绍 一.cookie: 在网站中,http请求是无状态的.也就是说即使第一次和服务器连接后并且登录成功后,第二次请求服务器依然不能知道当前请求是哪个用户. ...
- 关于neo4j的嵌入式和驱动包模式该如何选择,还请解惑
看了网上的一些资料和Neo4j权威指南这本书.与图遍历相关的介绍都是基于嵌入式模式下的java Api.但是个人觉得在实际的项目中,嵌入式的模式,代码必须放在数据库所在服务器上,且服务器的启停操作都在 ...
- 2019.9.24 csp-s模拟测试51(a) 反思总结
T1:还在头铁,顺便复习了一下lct[虽然这题用不上因为复杂度不对] 头铁结束. 虽然题目存在换根的操作,实际上并不用真的换根. 2操作中求lca的时候只要考虑原树上root和x.y的lca以及x,y ...
- 根据一个分类id 获取这个分类底下所有子分类的商品信息,根据下面方法查询出所有有关分类id 再 根据这些id去商品表里查询所有商品信息
/** * 检测该分类下所有子分类,并输出ID(包括自己) * 数据库字段 catid pid */ function getChildrenIds ($sort_id){ include_once ...
- jquery tooltip.js
1.引用关联的js脚本 <script type="text/javascript" src="script/jquery-1.3.2.min.js"&g ...
- 【python之路16】作业
#!usr/bin/env python # -*- coding:utf-8 -*- # 数据库中原有 old_dict = { "#1": {'hostname': 'c1', ...
- 从0开始学习 GitHub 系列之「02.加入 GitHub」
转载地址: http://blog.csdn.net/googdev/article/details/52787586 看完昨天的文章「从0开始学习 GitHub 系列之「初识 GitHub」」估计不 ...
- C++之正则表示,字符串是否为全字母或者全数字
bool isLetter(std::string& inputtext){ tr1::regex reg("^[A-Za-z]+$"); bool bValid = tr ...
- 洛谷P1929 迷之阶梯
P1929 迷之阶梯 题目描述 在经过地球防卫小队的数学家连续多日的工作后,外星人发的密码终于得以破解.它 告诉我们在地球某一处的古老遗迹中,存在有对抗这次灾难的秘密武器.防卫小队立即赶 到这处遗迹. ...
- 为什么要用Spring的依赖注入
最近写了一些Spring项目,用到了依赖注入,但是不知道为甚么要用,后来在知乎上看了一些大家的回答,觉得很精辟,遂简单总结一下. 主要是实现类之间的解耦,假如A类依赖B类,在实例化A类的话也要new一 ...