今天考 shanganze 大蛇的
菜菜...
T1
暴力就 \(n^3\) 判断
思考正解 :
发现矩阵乘法的过程很典,我们肯定不能优化 我要优化矩乘
那我们可以想类似哈希,将信息压缩
我想到两种
第一种,也是先想到的
算出 \(c\) 矩阵的行列和,与 \(a \times b\) 比较即可
有人就要问了,这不是 \(n^3\) 的 ?
观察矩阵乘法形式,用前缀和优化即可 (\(n ^ 3\))
但这个显然会被卡(好像不容易被赛事卡),而且需要额外的前缀和数组,常数还大
有没有什么更简单的做法
有的有的
考虑矩阵乘是 \(n^3\) 的,但向量就 \(n^2\)
可以随一个向量验证
正确性很高
T2
注意题目是让求每个子树(我读错了)
很厉害的题目
暴力可以写 \(O(n ^ 2)\) 的背包,然后发现是个闵和的形式,听说可以用平衡树维护
但常数有亿点大
正解是贪心
考虑最大的点一定只选自己,而操作其父亲时,一定会选他,那我们就可以直接将他和父亲合并
一个点被合并时就是答案
相当于是一个不断选最大点的过程
用并查集和优先队列维护即可
T3
题目很明了 先打个暴力
发现一段区间的贡献与区间和和长度有关
相同长度的区间只有最大值有用
那我们可以直接将信息压缩,假设我们已经得到任意长度和的最大值
发现询问就是若干一次函数求最大值,就是李超树的形式
但短暂思考,我们也可以用更为简单的单调队列实现
询问复杂度单次 \(log\) , 够用
关键在于预处理
要求出所有长度,考虑分治
每次处理跨区间的贡献,处理两边的前后缀和数组 \(f , g\),答案形式如下
\(h_{i + j}\) <- \(max(f_i + g_j)\)
是标准闵可夫斯基和形式
但 \(f , g\) 不是凸函数,但发现若不在凸包上一定不在最后的答案数组上,所以可以直接求凸包
T4.
区间和 \(< 0\) 即无解,否则一定有解
这题考虑答案不超过 \(2 \times len\) ,因为可以走来回,一定合法
考虑怎样减少代价
假设 \(s \le t\) ,由于每个位置都要经过,所以 \(\le s , \ge t\),最优即为经过两遍
设 \(sum_x\) 表示,左端点 \(l\) 到 \(x\) 的和
而他们中间,如果一个点 \(x\) , \(sum_x \ge 0\) 那只用经过一遍
而 \(sum_x < 0\) 最优一定是到后面最近的一个 \(sum_y \le 0\) ,再回来绕一遍,需要走三遍
那贡献就明了了,把前缀和 \(\le 0\) 的位置赋成 1 , 否则赋成 -1 ,走两遍减去最大区间和
对 \(sum_{l - 1}\) 扫描线
然后小白逛公园即可
注意我们是对 \(s \le t\) 讨论,实际上我们只需要正反做两遍即可
点击查看代码
/* みやみず みつは */
/* 任何错误都有迹可循 */
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N = 3e6 + 10;
inline int read() {
int x=0,f=1;char ch=getchar();
while (ch<'0'||ch>'9'){if (ch=='-') f=-1;ch=getchar();}
while (ch>='0'&&ch<='9'){x=x*10+ch-48;ch=getchar();}
return x*f;
}
int n , m;
int h[N] , sum[N];
struct qii {
int l , r , h , id;
}qm[N]; int cnt;
int ans[N] , id[N];
bool cmp(qii x , qii y) { return x.h < y.h; }
bool hhh(int x , int y) { return sum[x] < sum[y]; }
struct seg {
int lmx , rmx , mx , sum;
void init(int tg) { lmx = rmx = mx = sum = tg; }
seg operator + (const seg &x) const {
return (seg){max(lmx , sum + x.lmx) , max(x.rmx , rmx + x.sum) ,
max({mx , x.mx , rmx + x.lmx}) , sum + x.sum};
}
}t[N];
#define mid (l + r) / 2
#define ls p << 1
#define rs p << 1 | 1
void pushup(int p) { t[p] = t[ls] + t[rs]; }
void build(int p , int l , int r) {
if(l == r) { t[p].init(1); return ; }
build(ls , l , mid); build(rs , mid + 1 , r);
pushup(p);
}
void mdf(int p , int l , int r , int x) {
if(l == r) { t[p].init(-1); return ; }
if(x <= mid) mdf(ls , l , mid , x);
else mdf(rs , mid + 1 , r , x);
pushup(p);
}
seg qry(int p , int l , int r , int x , int y) {
if(l >= x && r <= y) return t[p];
if(y <= mid) return qry(ls , l , mid , x , y);
if(x > mid) return qry(rs , mid + 1 , r , x , y);
return qry(ls , l , mid , x , y) + qry(rs , mid + 1 , r , x , y);
}
void init() {
cnt = 0;
for(int i = 1;i <= n;i ++) id[i] = i;
for(int i = 1;i <= m;i ++) ans[i] = 1e18;
}
void rev() {
reverse(h + 1 , h + n + 1);
for(int i = 1;i <= cnt;i ++) qm[i].l = n - qm[i].l + 1 , qm[i].r = n - qm[i].r + 1;
for(int i = 1;i <= cnt;i ++) swap(qm[i].l , qm[i].r);
}
void ii() {
build(1 , 1 , n);
for(int i = 1;i <= n;i ++) sum[i] = h[i] + sum[i - 1];
for(int i = 1;i <= cnt;i ++) qm[i].h = sum[qm[i].l - 1];
sort(qm + 1 , qm + cnt + 1 , cmp);
sort(id + 1 , id + n + 1 , hhh);
int H = 1;
for(int i = 1;i <= cnt;i ++) {
while(H <= n && sum[id[H]] < qm[i].h) mdf(1 , 1 , n , id[H]) , H ++;
ans[qm[i].id] = min(ans[qm[i].id] ,
-qry(1 , 1 , n , qm[i].l , qm[i].r - 1).mx + 2 * (qm[i].r - qm[i].l));
}
}
signed main() {
int T; T = read();
while(T --) {
n = read(); m = read(); init();
for(int i = 1;i <= n;i ++) h[i] = read();
for(int i = 1;i <= n;i ++) sum[i] = h[i] + sum[i - 1];
for(int i = 1;i <= m;i ++) {
int l = read() , r = read();
if(sum[r] - sum[l - 1] < 0) { ans[i] = -1; continue; }
if(l == r) { ans[i] = 0; continue; }
qm[++ cnt] = (qii) {l , r , sum[l - 1] , i};
}
ii(); rev(); ii();
for(int i = 1;i <= m;i ++) printf("%lld\n",ans[i]);
}
return 0;
}