51nod 1712 区间求和
题意
给你一个长度为 \(n\) 的序列,定义这个序列的权值为 $ \sum_{1 \leq i<j \leq n} a_j - a_i $。
现在给你一个长度为 \(n\) 的序列,当$ a_i=a_j $时,将 $ a_i, a_{i+1}, ... , a_j $ 提出当做一个序列,计算它的权值。统计所有这样的区间的权值和。答案模 \(2^{32}\)。
想法
先考虑对于原题的做法,即对于区间 \([i, j]\) ,每一位都要乘一个系数,即算每一位的系数大小即可。对于 \(i \leq k \leq j\) ,系数 \(= -(j-k) + (k-i) = 2k-(i+j)\) 。回到这道题,对于 \(a_k\),有多少个区间 \([i, j](a_i=a_j, i \leq j)\) 包含了 \(a_k\) 就要加多少个 \(2k\),且要减去这些区间的左端点和右端点的值。
由于减去右端点的值相当于将整个序列翻转然后去减左端点的值,故这里我们只考虑左端点。减右端点的情况我们倒着跑一边即可。
首先我们可以预处理出每个数从左往右(\(lrk\))/从右往左(\(rrk\))数是第几次出现,它的前驱(\(pre\))/后继(\(next\))分别在哪。
方便起见,我们不妨考虑 \([i, i]\) 这样的区间也合法。考虑序列3 3 3 3
, \(f_i\) 表示要减的左端点的值的和。\(f_1 = 1*4 = 4, f_2 = f_1 + 2*3 - 1 = 9, f_3 = f_2 + 3*2 - (1 + 2)=12, f_4 = f_3 + 4*1 - (1 + 2 + 3)=10\) 。观察可知每次加上了一个 \(i*rrk[i]\) ,又减掉了 \(i\) 之前出现的值为 \(a_i\) 的下标和。为什么呢?因为我们一开始假想 \(i\) 到序列末尾都覆盖上了 \(i\) ,中间遇到一个 \(a_i\) 的时候就要让其中的一个区间停止更新。那么我们定义 \(decA[i]\) 表示 \(i\) 之前出现的值为 \(a_i\) 的下标和,转移方程为 \(decA[i] = decA[pre[i]] + pre[i]\)。一般的,3
和3
之间还有很多其他的数,那么我们减掉 \(decA[i]\) 的时候不是下一次 \(a[i]\) 出现的时候,而是 \(i+1\) 就必须减掉,不然覆盖的区间就是 \([i, next[i])\) 了,实际上应该是 \([i, i]\) 。
故可以得出转移方程 $$f[i] = f[i-1] + i*rrk[i] - decA[next[i-1]]$$
这里有一个问题,就是如果 \(rrk[i] = 1\) 的时候 \(next[i]\) 不存在, 减 \(decA[next[i]]\) 的时候会出错。那我们自己造一个 \(next[i]\) 嘛...特判一下就好了。
最后是 \(2k\) 的个数,由于以上我们统计的是左端点的值的总和,那么我们把这个值改成 \(1\) 不就是区间的个数了吗。 \(decA[i]\) 的转移方程改成 \(decA[i] = decA[pre[i]] + 1\) 即可。
怕gg所以写了个快速乘...不过好像不用也没问题...
记得用unsigned int
存答案,最好写个读入优化。
Code
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iostream>
#define ll long long
#define db double
#define uint unsigned int
#define N 3000100
#define max(a, b) ((a) > (b) ? (a) : (b))
#define min(a, b) ((a) < (b) ? (a) : (b))
#define swap(T, a, b) ({T ttt = a; a = b; b = ttt;})
int n, lrk[N], rrk[N], pre[N], next[N], pos[N];
uint a[N], decA[N], decB[N], f[N], g[N], Ans = 0;
void G(uint &w) {
w = 0; char c = getchar();
while (c > '9' || c < '0') c = getchar();
while (c >= '0' && c <= '9') { w = w * 10 + c - '0'; c = getchar(); }
}
uint Mult(uint a, uint b)
{
uint s = 0;
while (b) {
if (b & 1) s += a;
a += a; b >>= 1;
}
return s;
}
int main()
{
scanf("%d", &n);
for (int i = 1; i <= n; i++)
G(a[i]);
memset(pos, 0, sizeof(pos));
for (int i = 1; i <= n; i++)
{
pre[i] = pos[a[i]]; pos[a[i]] = i;
lrk[i] = lrk[pre[i]] + 1;
decA[i] = decA[pre[i]] + pre[i];
}
memset(pos, 0, sizeof(pos));
for (int i = n; i >= 1; i--)
{
next[i] = pos[a[i]]; pos[a[i]] = i;
rrk[i] = rrk[next[i]] + 1;
decB[i] = decB[next[i]] + next[i];
}
for (int i = 1; i <= n; i++)
{
if (rrk[i] == 1)
{
next[i] = i+n; pre[i+n] = i;
lrk[next[i]] = lrk[i] + 1;
decA[i+n] = decA[i] + i;
}
if (lrk[i] == 1)
{
pre[i] = i+n*2; next[i+n*2] = i;
rrk[pre[i]] = rrk[i] + 1;
decB[i+n*2] = decB[i] + i;
}
}
for (int i = 1; i <= n; i++)
f[i] = f[i-1] + i*rrk[i] - decA[next[i-1]];
for (int i = n; i >= 1; i--)
g[i] = g[i+1] + i*lrk[i] - decB[pre[i+1]];
for (int i = 1; i <= n; i++)
f[i] = f[i] + g[i] - i*2;
memset(g, 0, sizeof(g));
memset(decA, 0, sizeof(decA));
for (int i = 1; i <= n; i++)
{
decA[i] = decA[pre[i]] + (pre[i] >= 1 && pre[i] <= n);
if (rrk[i] == 1) decA[i+n] = decA[i] + 1;
}
for (int i = 1; i <= n; i++)
{
g[i] = g[i-1] + rrk[i] - decA[next[i-1]];
f[i] = Mult(g[i] - 1, i) * 2 - f[i];
Ans += Mult(f[i], a[i]);
}
std::cout << Ans << std::endl;
return 0;
}
51nod 1712 区间求和的更多相关文章
- 51Nod 1680 区间求和 树状数组
题意: 给出一个长度为\(n\)的数列\(A_i\),定义\(f(k)\)为所有长度大于等于\(k\)的子区间中前\(k\)大数之和的和. 求\(\sum_{k=1}^{n}f(k) \; mod \ ...
- 【51nod】区间求和
LYK在研究一个有趣的东西. 假如有一个长度为n的序列,那么这个序列的权值将是所有有序二元组i,j的 Σaj−ai 其中1<=i<j<=n. 但是这个问题似乎太简单了. 于是LYK想 ...
- 51nod 1680区间求和 (dp+树状数组/线段树)
不妨考虑已知一个区间[l,r]的k=1.k=2....k=r-l+1这些数的答案ans(只是这一个区间,不包含子区间) 那么如果加入一个新的数字a[i](i = r+1) 则新区间[l, i]的答案为 ...
- POJ 2823 Sliding Window 线段树区间求和问题
题目链接 线段树区间求和问题,维护一个最大值一个最小值即可,线段树要用C++交才能过. 注意这道题不是求三个数的最大值最小值,是求k个的. 本题数据量较大,不能用N建树,用n建树. 还有一种做法是单调 ...
- POJ 3468 A Simple Problem with Integers(线段树 成段增减+区间求和)
A Simple Problem with Integers [题目链接]A Simple Problem with Integers [题目类型]线段树 成段增减+区间求和 &题解: 线段树 ...
- vijos1740 聪明的质监员 (二分、区间求和)
http://www.rqnoj.cn/problem/657 https://www.vijos.org/p/1740 P1740聪明的质检员 请登录后递交 标签:NOIP提高组2011[显示标签] ...
- LightOJ 1112 Curious Robin Hood (单点更新+区间求和)
http://lightoj.com/volume_showproblem.php?problem=1112 题目大意: 1 i 将第i个数值输出,并将第i个值清0 2 i v ...
- POJ 3468 A Simple Problem with Integers(线段树区间求和)
Description You have N integers, A1, A2, ... , AN. You need to deal with two kinds of operations. On ...
- poj3468 A Simple Problem with Integers(线段树模板 功能:区间增减,区间求和)
转载请注明出处:http://blog.csdn.net/u012860063 Description You have N integers, A1, A2, ... , AN. You need ...
随机推荐
- bzoj violet系列 (2708~2725)
cbh大爷说:写博客不能弃坑. orz cbh 那我就来更新博客了. violet这个系列的题好神啊……出题人好劲啊…… ……怎么最近都在理性愉悦啊…… 另外bzoj400题纪念~ 2708: [Vi ...
- IIS网站或系统验证码不显示问题——"使用了托管的处理程序,但是未安装或未完整安装 ASP.NET"
在IIS上发布了一个系统,但是登陆页面的验证码图片一直出不来,尝试了各种办法,权限.路径.继承父类路径等都不管用,进入Login.html,对着无验证码图片的图标,右键复制图片的网址,粘贴到地址栏,出 ...
- (lintcode全部题目解答之)九章算法之算法班题目全解(附容易犯的错误)
--------------------------------------------------------------- 本文使用方法:所有题目,只需要把标题输入lintcode就能找到.主要是 ...
- linux查看安装文件
rpm -qa jdk 查看名字包含"jdk"的已安装的文件 which java 查看java命令的所在目录 rpm -qf `which java` 查看java命令所对应的安 ...
- SQL Server子查询实例
例子一 SELECT COUNT(*) FROM ( SELECT [PersonID] FROM [tbiz_AssScore] GROUP BY PersonID ) M 语法说明: 1).FRO ...
- Linux 命令备忘录(CentOS 7)
创建目录testdir: mkdir testdir 进入目录testdir:cd testdir 在testdir中创建空文件 1: touch 1 在testdir中创建空文件 2: t ...
- SWFUpload多图上传、C#后端跨域传文件带参数
前几天工作中用到了SWFUpload上传图片,涉及到跨域,因为前端无法实现跨域,所以只能把文件传到后端进行跨域请求,整理分享下. 效果图 前端 html部分 <!DOCTYPE html> ...
- java+eclipse+selenium环境搭建
这几天在学selenium,大头虾的我.安装环境还是遇到了挺多问题,赶紧来记录下.不然下次又...(参考虫师的<Selenium2 Java自动化测试实战>),就随便写写加深下自己的印象. ...
- 338. Counting Bits
https://leetcode.com/problems/counting-bits/ 给定一个非负数n,输出[0,n]区间内所有数的二进制形式中含1的个数 Example: For num = 5 ...
- STL源码学习----lower_bound和upper_bound算法
转自:http://www.cnblogs.com/cobbliu/archive/2012/05/21/2512249.html 先贴一下自己的二分代码: #include <cstdio&g ...