线段树教做人系列(3) HDU 4913
题意及思路看这篇博客就行了,讲得很详细。
下面是我自己的理解:
如果只有2,没有3的话,做法就很简单了,只需要对数组排个序,然后从小到大枚举最大的那个数。那么它对答案的贡献为(假设这个数排序后的位置是pos)2 ^ (pos - 1) * 2 ^ a[pos]。意思是a[pos]这个数必选,其它比它小的数可选可不选,有2^(pos - 1)种情况。现在相当于变成了一个二维的问题。对于这种问题,我们常见的做法是确定一维,在从前往后扫描某一维时加上另一维对答案的贡献。对于这个题,我们可以按数组b从小到大排序,去计算a的贡献。假设现在扫描到的第pos个位置(二元组(a[i], b[i])已经按数组b排序),我们考虑来计算a[i]对答案的贡献。a对答案的贡献分为2部分,一部分是之前已经出现过的,小于等于a[i]的值,假设一共有x个,那么这部分的贡献为(2 ^ x * 2 ^ a[i]),那么大于a[i]的部分呢?其实和这个式子差不多。对于每个已经出现过,并且大于a[i]的a[j],假设已经出现过的比a[j]小的数有y个,那么贡献为2 ^ (y - 1) * 2 * a[j]。为什么是y - 1? 因为a[i]是必选的。通过观察,我们可以发现,每一个a[j]对答案的贡献,取决当前已经出现过的数中有多少个比它小的数,所以我们可以这样维护:在每次插入一个值时,先询问在这个数之前出现了多少个数(假设有x个),然后插入2 ^ x * 2 ^ a[i],询问[i,n]的区间和,就是这一阶段的答案。之后,要把[i + 1,n]中的数乘2,因为他们的前面都多了一个a[i]。
代码:
#include<bits/stdc++.h>
#define ls(x) (x << 1)
#define rs(x) ((x << 1) | 1)
#define LL long long
using namespace std;
const int maxn = 100010;
const LL mod = 1000000007;
struct node{
int x, y, rank;
};
bool cmp1(node x, node y) {
return x.x == y.x ? x.y < y.y : x.x < y.x;
}
bool cmp2(node x, node y) {
return x.y == y.y ? x.x < y.x : x.y < y.y;
}
node a[maxn];
struct SegementTree {
LL sum, cnt, lz;
};
SegementTree tr[maxn * 4];
LL qpow(LL x, LL y) {
LL ans = 1;
for (; y; y >>= 1) {
if(y & 1) ans = (ans * x) % mod;
x = (x * x) % mod;
}
return ans;
}
void pushup(int x) {
tr[x].sum = (tr[ls(x)].sum +tr[rs(x)].sum) % mod;
tr[x].cnt = (tr[ls(x)].cnt + tr[rs(x)].cnt) % mod;
}
void maintain(int x, int y) {
tr[x].sum = (tr[x].sum * qpow(2, y)) % mod;
tr[x].lz += y;
}
void pushdown(int x) {
if(tr[x].lz) {
if(tr[ls(x)].cnt) maintain(ls(x), tr[x].lz);
if(tr[rs(x)].cnt) maintain(rs(x), tr[x].lz);
tr[x].lz = 0;
}
}
void build(int x, int l, int r) {
if(l == r) {
tr[x].sum = tr[x].cnt = 0;
return;
}
int mid = (l + r) >> 1;
build(ls(x), l, mid);
build(rs(x), mid + 1, r);
pushup(x);
}
void update_cnt(int x, int l, int r, int pos, int y, int z) {
if(l == r) {
tr[x].cnt = 1;
tr[x].sum = (qpow(2, y) * qpow(2, z)) % mod;
return;
}
pushdown(x);
int mid = (l + r) >> 1;
if(pos <= mid) update_cnt(ls(x), l, mid, pos, y, z);
else update_cnt(rs(x), mid + 1, r, pos ,y, z);
pushup(x);
}
void update_sum(int x, int l, int r, int ql, int qr) {
if(l >= ql && r <= qr) {
tr[x].lz++;
tr[x].sum = (tr[x].sum * 2) % mod;
return;
}
pushdown(x);
int mid = (l + r) >> 1;
if(ql <= mid) update_sum(ls(x), l, mid, ql, qr);
if(qr > mid) update_sum(rs(x), mid + 1, r, ql, qr);
pushup(x);
}
LL query_cnt(int x, int l, int r, int ql, int qr) {
if(l >= ql && r <= qr) {
return tr[x].cnt;
}
int mid = (l + r) >> 1;
pushdown(x);
LL ans = 0;
if(ql <= mid) ans += query_cnt(ls(x), l, mid, ql, qr);
if(qr > mid) ans += query_cnt(rs(x), mid + 1, r, ql, qr);
return ans;
}
LL query_sum(int x, int l, int r, int ql, int qr) {
if(l >= ql && r <= qr) {
return tr[x].sum;
}
int mid = (l + r) >> 1;
LL ans = 0;
pushdown(x);
if(ql <= mid) ans += query_sum(ls(x), l, mid, ql, qr);
if(qr > mid) ans += query_sum(rs(x), mid + 1, r, ql, qr);
return ans % mod;
}
int main() {
int n;
while(~scanf("%d", &n)) {
for (int i = 1; i <= n; i++) {
scanf("%d%d", &a[i].x, &a[i].y);
}
sort(a + 1, a + 1 + n, cmp1);
for (int i = 1; i <= n; i++) {
a[i].rank = i;
}
sort(a + 1, a + 1 + n, cmp2);
build(1, 1, n);
LL ans = 0;
for (int i = 1; i <= n; i++) {
LL tmp = query_cnt(1, 1, n, 1, a[i].rank);
update_cnt(1, 1, n, a[i].rank, tmp, a[i].x);
ans = (ans + query_sum(1, 1, n, a[i].rank, n) * qpow(3, a[i].y) % mod) % mod;
if(a[i].rank != n)
update_sum(1, 1, n, a[i].rank + 1, n);
}
printf("%lld\n", ans);
}
}
线段树教做人系列(3) HDU 4913的更多相关文章
- 线段树教做人系列(2)HDU 4867 XOR
题意:给你一个数组a,长度为.有两种操作.一种是改变数组的某个元素的值,一种是满足某种条件的数组b有多少种.条件是:b[i] <= a[i],并且b[1]^b[2]...^b[n] = k的数组 ...
- Codeforces 719E (线段树教做人系列) 线段树维护矩阵
题面简洁明了,一看就懂 做了这个题之后,才知道怎么用线段树维护递推式.递推式的递推过程可以看作两个矩阵相乘,假设矩阵A是初始值矩阵,矩阵B是变换矩阵,求第n项相当于把矩阵B乘了n - 1次. 那么我们 ...
- 线段树教做人系列(1)HDU4967 Handling the Past
题意:给你n组操作,分别为压栈,出栈,询问栈顶元素.每一组操作有一个时间戳,每次询问栈顶的元素的操作询问的是在他之前出现的操作,而且时间戳小于它的情况.题目中不会出现栈为空而且出栈的情况. 例如: p ...
- Codeforces 1136E Nastya Hasn't Written a Legend (线段树教做人系列)
题意:有一个数组a和一个数组k,数组a一直保持一个性质:a[i + 1] >= a[i] + k[i].有两种操作:1,给某个元素加上x,但是加上之后要保持数组a的性质.比如a[i]加上x之后, ...
- 线段树(单点更新,区间查询) HDU 1754 I Hate It
题目链接 线段树的模板 #include<iostream> #include<cstdio> #include<cmath> #include<algori ...
- 线段树(区间合并)HDU - 1540
题意:输入n,m,给定n个相互连通的村庄,有m个操作,D x,表示破坏x村庄使其与相邻的两个村庄不相通,R 表示修复上一个被破坏的村庄,与相邻的两个村庄联通.Q x表示与x相连的村庄有多少个. 思路: ...
- (线段树 区间查询)The Water Problem -- hdu -- 5443 (2015 ACM/ICPC Asia Regional Changchun Online)
链接: http://acm.hdu.edu.cn/showproblem.php?pid=5443 The Water Problem Time Limit: 1500/1000 MS (Java/ ...
- (线段树)Just a Hook -- hdu -- 1689
链接: http://acm.hdu.edu.cn/showproblem.php?pid=1698 思路: 我的想法很简单,像上一题一样从后面向前面来算,前面已经覆盖的,后面自然不能再来计算了,具体 ...
- 线段树(区间修改、区间查询) HDU 1754 I Hate It
I Hate It Time Limit: 9000/3000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)Total S ...
随机推荐
- python中的列表和字典(一)
一. 列表 1. 列表的定义 [] 2. 列表特征:有序列表,可以包含任意内容,可以重复 3. 列表的赋值(顺序赋值):listA = [A, B, C] 4. 列表的取值:list[index] ...
- zoj3229 有源汇上下界最大流
题意:有一个人每天给妹子拍照,每个妹子有最少拍照数,每天有最大拍照数,每天只能给某些特定的妹子拍照,求最大拍照数 题解:很容易看出来的有源汇上下界最大流,对于有源汇 的上下界最大流,我们按照无源汇的操 ...
- http协议学习系列(一个博文链接)
深入理解HTTP协议(转) http协议学习系列(转自:http://www.blogjava.net/zjusuyong/articles/304788.html) 1. 基础概念篇 1.1 介绍 ...
- SpringBoot_07_Springboot test 使用mockito进行web测试
一.前言 使用mockito测试框架可以方便的进行web测试 二.用法实例 package com.ray.weixin.qy.controller; import com.ray.weixin.qy ...
- PHP学习之数组Array操作和键值对操作函数(一)
PHP 中的数组实际上是一个有序映射.映射是一种把 values关联到 keys 的类型.此类型在很多方面做了优化,因此可以把它当成真正的数组,或列表(向量),散列表(是映射的一种实现),字典,集合, ...
- MariaDB10.1找回密码
C:\Program Files\MariaDB 10.1\data下面的my.ini文件,在[mysqld]节点下,增加一句: skip-grant-tables 重启MariaDB服务(mysq ...
- JSP Servlet中的Request和Response的简单研究
本文参考了几篇文章所得,参考目录如下: 1.http://www.cnblogs.com/guangshan/p/4198418.html 2.http://www.iteye.com/problem ...
- MySQL最新版本 MySQL5.7.11 批量自动化一键式安装(转)
--背景云端 以前都喜欢了源码安装MySQL,总觉得源码是高大上的事情,不过源码也需要时间,特别是make的时候,如果磁盘和cpu差的话,时间很长很长,在虚拟机上安装mysql尤其甚慢了. 现在业务发 ...
- linux下安装composer
在linux下使用comoser命令,但是提示composer command not found 那么就是当前环境中没有composer 学习源头: https://blog.csdn.net/gb ...
- 蓝桥杯 算法训练 ALGO-156 表达式计算
算法训练 表达式计算 时间限制:1.0s 内存限制:256.0MB 问题描述 输入一个只包含加减乖除和括号的合法表达式,求表达式的值.其中除表示整除. 输入格式 输入一行,包含一个表达式. 输 ...