题意

给你一个长度为 \(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]\)。一般的,33之间还有很多其他的数,那么我们减掉 \(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 区间求和的更多相关文章

  1. 51Nod 1680 区间求和 树状数组

    题意: 给出一个长度为\(n\)的数列\(A_i\),定义\(f(k)\)为所有长度大于等于\(k\)的子区间中前\(k\)大数之和的和. 求\(\sum_{k=1}^{n}f(k) \; mod \ ...

  2. 【51nod】区间求和

    LYK在研究一个有趣的东西. 假如有一个长度为n的序列,那么这个序列的权值将是所有有序二元组i,j的 Σaj−ai 其中1<=i<j<=n. 但是这个问题似乎太简单了. 于是LYK想 ...

  3. 51nod 1680区间求和 (dp+树状数组/线段树)

    不妨考虑已知一个区间[l,r]的k=1.k=2....k=r-l+1这些数的答案ans(只是这一个区间,不包含子区间) 那么如果加入一个新的数字a[i](i = r+1) 则新区间[l, i]的答案为 ...

  4. POJ 2823 Sliding Window 线段树区间求和问题

    题目链接 线段树区间求和问题,维护一个最大值一个最小值即可,线段树要用C++交才能过. 注意这道题不是求三个数的最大值最小值,是求k个的. 本题数据量较大,不能用N建树,用n建树. 还有一种做法是单调 ...

  5. POJ 3468 A Simple Problem with Integers(线段树 成段增减+区间求和)

    A Simple Problem with Integers [题目链接]A Simple Problem with Integers [题目类型]线段树 成段增减+区间求和 &题解: 线段树 ...

  6. vijos1740 聪明的质监员 (二分、区间求和)

    http://www.rqnoj.cn/problem/657 https://www.vijos.org/p/1740 P1740聪明的质检员 请登录后递交 标签:NOIP提高组2011[显示标签] ...

  7. LightOJ 1112 Curious Robin Hood (单点更新+区间求和)

    http://lightoj.com/volume_showproblem.php?problem=1112 题目大意: 1 i        将第i个数值输出,并将第i个值清0 2 i v     ...

  8. 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 ...

  9. poj3468 A Simple Problem with Integers(线段树模板 功能:区间增减,区间求和)

    转载请注明出处:http://blog.csdn.net/u012860063 Description You have N integers, A1, A2, ... , AN. You need ...

随机推荐

  1. 我理解的Sitecore开发流程

    Sitecore是一个强大的支持快速开发CMS建站的平台,进入sitecore后台可以看到 它主要有3个元素: 1.Templates模板 Data template:类似于面向对象编程中的类或结构体 ...

  2. 求两圆相交部分面积(C++)

    已知两圆圆心坐标和半径,求相交部分面积: #include <iostream> using namespace std; #include<cmath> #include&l ...

  3. 在Extjs中对日期的处理,以及在后端数据在SQL语句的判断处理

    jsp页面可选择时间: { xtype : 'datefield', id : 'START_CREATION_DATE_', format : 'Y-m-d H:i:s', submitFormat ...

  4. win7系统下 自带的定时关机

    进入cmd下,输入shutdown -s -t 600 以上例子代表的是10分钟后自动关机 -s代表定时关机 -t代表着定时,时间以秒为单位一分钟60s 输入完后按enter 定时关机设置完成 当想取 ...

  5. java模拟post方式提交表单实现图片上传【转】

     转自:http://blog.csdn.net/5iasp/article/details/8669644 模拟表单html如下:   <form action="up_result ...

  6. c#获取外网IP地址的方法

    1.如果你是通过路由上网的,可以通过访问ip138之类的地址来获取外网IP 2.如果是通过PPPOE拨号上网的,可以使用以下代码获取IP //获取宽带连接(PPPOE拨号)的IP地址,timeout超 ...

  7. jeecms3.0.4版本 详解请求如何找到首页(转)

    第一步:发送http://localhost:8080/emisstrade/ 请求 第二步:首先进入配置文件web.xml, <context-param> <param-name ...

  8. select初始化添加option,通过标签给出回显值,由于回显值和初始化值option中有一个值重复,去重等问题!

    第一张图片: 第二张图片 /** *该方法是为了去重,所谓去重就是 因为回显给select附上了值并设置为selected选中状态,而在我们初始化所有的select添加option元素中于回显的值重复 ...

  9. vba 相关

    返回当前默认文件路径: Application.DefaultFilePath 返回应用程序完整路径 Application.Path 返回当前工作薄的路径 ThisWorkbook.Path App ...

  10. myeclipse maven 安装

    myeclipse 上安装 Maven3   环境准备: JDK 1.6.45 Maven 3.0.5 myeclipse 8.5 安装 Maven 之前要求先确定你的 JDK 已经安装配置完成.Ma ...