题解:luoguP15969 彩色装饰
打比赛的时候碰到这道题,觉得小K真的好闲啊真是一道情景真实有趣的好题啊!
题意解读
我们可以把题意转化为给一个颜色序列,对小 \(K\) 刷上去的每个颜色 k,把任意区间 \([l,r]\) 染成 \(k\),求所有 \(\dfrac{n(n+1)}{2}\) 种染色后,美丽度之和。
注:美丽度 \(=\)
\[\left(\sum_{i=2}^n [a_i \neq a_{i-1}]\right) + 1
\]
思路
根据转化的题意,我们可以把问题拆到最简单:
设当前询问小 \(K\) 在丝带上刷的是颜色 \(k\),总段数 $ T=\dfrac{n(n+1)}{2}\(,\)f(i,k)$ 表示有多少个区间 \([l,r]\),染成 \(k\) 后,使得新的 \(a[i]\) 和 \(a[i-1]\) 颜色不同。
我们套上美丽度公式,则对于当前询问的每段的美丽度可以表示为 \(\sum_{i=2}^{n} f(i,k)\),根据我们之前的定义,这里的美丽度的和就可以表示为:
\[\sum\limits_{i=2}^{n} f(i,k) + T
\]
同时根据 \(f(i,k)\) 的定义,我们可以把它表示成:
当 \(2\le i\le n\) 时完全不覆盖 \(i\) 和 \(i - 1\) 的区间数量加上只覆盖 \(i\) 以左的区间数量加上只覆盖 \(i-1\) 以右的区间数量。
代码实现
注:变量与数组的用途:
\(le[i]\) 表示满足 \(i\) 端点以右的区间数量;
\(ri[i]\) 表示满足 \(i\) 端点以左的区间数量;
\(tot\) 表示所有相邻对 \(i\) 中,原本颜色不同的那些对它们各自“完全不覆盖区间数”的总和;
\(suml\) 表示所有“只染右边”的区间数量的总和;
\(sumr\) 表示所有“只染左边”的区间数量的总和。
代码:
#include <bits/stdc++.h>
#define ll long long //懒人必备定义
using namespace std;
const int N = 1e6 + 5;//定义常量,方便数组定义
ll n, m, t, sumr, suml, ans, tot, a[N],le[N],ri[N];//不开 long long 见祖宗
int main() {
scanf("%lld %lld", &n, &m);//注意要用 %lld!!!
for (int i = 1; i <= n; i++) scanf("%lld",&a[i]);
t = (ll)1 * n * (n + 1) / 2;//总段数
for (int i = 2; i <= n; i++) {
int x = a[i - 1], y = a[i];//记录左边的点和右边的点
//区间会分成四种情况 1、完全不染 i 和 i-1 的;2、只染i以左的部分;3、只染 i-1 以右的区间数量;4、只染 i 和 i-1
ll l = (ll)1 * (i - 2) * (i - 1) / 2;//原来的只在左边的区间数量
ll r = (ll)1 * (n - i) * (n - i + 1) / 2;//原来的只在右边的区间数量
//l+r 表示完全不碰 i 和 i-1 的区间数量,对应上文中的 1
ll sl = i - 1;//当前只染 i 以左的区间数量,对应 2
ll sr = n - i + 1;//当前只染 i-1 以右的区间数量,对应 3
ll nowtot = (x != y) * (l + r);//如果原来颜色不同,这些区间的贡献为 1,反之为 0
tot += nowtot;//加上现在的贡献
le[x] += sr;//加上 x 以右的区间数量
ri[y] += sl;//加上 y 以左的区间数量
}
for (int i = 1; i <= m; i++) suml += le[i];//加上“只染右边”的区间数量
for (int i = 1; i <= m; i++) sumr += ri[i];//加上“只染左边” 的区间数量
for (int i = 1; i <= m; i++) {
ll nowsuml = suml - le[i];//只覆盖以左的区间数量
ll nowsumr = sumr - ri[i];//只覆盖以右的区间数量
ans = t + tot + nowsuml + nowsumr;//套公式
printf("%lld\n", ans);//输出也要使用 %lld
}
return 0;
}

浙公网安备 33010602011771号