CF1809C Sum on Subarrays 题解
一道黄题做了一上午我可以退役了。
构造题。由于直接构造并不是那么容易,所以考虑在一个初始元素全为 \(-1\) 的序列上构造。
考虑将一个负数改为正数 \(1000\) 对答案造成的贡献(因为取 \(1000\) 计算比较方便)。设在放置这个正元素之前已经放置了 \(x\) 个正元素,则这个正元素会造成 \(n-x\) 的贡献,也就是会创造 \(n-x\) 个新的正区间。
原因显然,由于负数均为 \(-1\),\(n\) 又小于等于 \(30\),\(1000\) 相当于正无穷,所以从这个元素到序列中的任何一个位置都是正区间。如果某一个位置已经是 \(1000\) 了,那么在插入之前的这个 \(1000\) 时已经计算了这一段区间,需要减去不计算,所以贡献为 \(n-x\)。
由于位置无关,我们尽量把 \(1000\) 往后放,如果再放一个 \(1000\) 就超过了正区间的数量,我们考虑在前面创造一些单独的正区间。设剩下的数为 \(s\),则我们将第一个数改为 \(200\),很明显,这也相当于一个正无穷。然后,我们要把第 \(s+1\) 个位置的数改为 \(-400\),消除 \(200\) 对后面的数的影响。由于之前放的数是 \(1000\),所以 \(-400\) 并不会影响其正无穷性质。
第一个数 \(200\) 可以把从第一个数开始到 \(s+1\) 以前的区间变成正区间,刚好是 \(s\) 个正区间,刚好符合要求。
#include <bits/stdc++.h>
using namespace std;
long long t,n,k,a[40];
int main()
{
scanf("%lld",&t);
while(t--)
{
scanf("%lld%lld",&n,&k);
long long now=n;
for(int i=1;i<=n;i++)a[i]=-1;
for(int i=n;i>=1;i--)
if(k>=now)a[i]=1000,k-=now,now--;
else break;
if(k!=0)a[1]=200,a[k+1]=-400;
for(int i=1;i<=n;i++)printf("%lld ",a[i]);
printf("\n");
}
return 0;
}

浙公网安备 33010602011771号