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 ...
随机推荐
- 如何在centos下部署Node环境
从今年开始,养车之家的PC 和 M端 的站点 ,服务器从tomcat全部改成了Node,无论在测试环境.准生产.正式环境,我们都需要在服务器进行Node的部署,在这里我给大家详细说明下Node在lin ...
- 推荐eclipse插件Properties Editor
需求:一般我们在做"国际化"功能时,我们需要properties中文表示方式用unicode表示.eclipse默认properties文件编辑器不方便查看,需要我们查看常常查找u ...
- DIV+CSS布局中主要CSS属性介绍
Float: Float属性是DIV+CSS布局中最基本也是最常用的属性,用于实现多列功能,我们知道<div>标签默认一行只能显示一个,而使用Float属性可以实现一行显示多个div的功能 ...
- 全部leetcode题目解答(不含带锁)
(记忆线:当时一刷完是1-205. 二刷88道.下次更新记得标记不能bug-free的原因.) 88-------------Perfect Squares(完美平方数.给一个整数,求出用平方数来 ...
- java问题排查总结
前些天发现:http://hellojava.info/这个站点,关于java问题排查分析总结线上故障总结其实是最有价值的,好的总结就是一个系统演进历史,是团队难得的积累沉淀. 花了不少时间看了下,顺 ...
- python address already in use
1)找到使用端口的进程pid netstat -lp 2)kill掉pid kill -9 1234
- 《Neural Network and Deep Learning》_chapter4
<Neural Network and Deep Learning>_chapter4: A visual proof that neural nets can compute any f ...
- extern
gcc编译器编译程序有四个阶段,预处理.编译.汇编.链接.预处理阶段会将源代码中的包含的头文件如stdio.h编译进来:编译阶段,gcc首先要检查代码的规范性.是否有语法错误等,以确定代码的实际要做的 ...
- python 装饰器
#!/usr/bin/env python3 #-*-encoding:utf-8-*- def w3(*args, **kwargs): ') def w1(): def ww1(func): de ...
- css3 自定义字体 @font-face
CSS3 @font-face 规则 在 CSS3 之前,web 设计师必须使用已在用户计算机上安装好的字体. 通过 CSS3,web 设计师可以使用他们喜欢的任意字体. 当您您找到或购买到希望使用的 ...