将(i, a[i])当作点,对a[i]这个维度扫描,在i这个维度上面建立扫描线
#include<bits/stdc++.h>
using namespace std;
const int N = 2e5+10;
array<int, 4> que[N];
array<int, 2> a[N];
// 线段树维护树链剖剖分的dfs序
struct Node{
int l, r;
int minn;
}tr[4*N];
void pushup(Node &F, Node L, Node R){
F.minn = min(L.minn, R.minn);
}
void pushup(int u){
pushup(tr[u], tr[u << 1], tr[u << 1 | 1]);
}
void build(int u, int l, int r){
if(l == r){
// 这里注意要加个id,获得序列上l位置的点的是那个点,然后获得他的权值
tr[u] = {l, r, 0x3f3f3f3f};
}
else{
tr[u] = {l, r, 0x3f3f3f3f};
int mid = l + r >> 1;
build(u<<1, l, mid), build(u<<1|1, mid+1, r);
pushup(u);
}
}
void modify(int u, int x, int v){
if(tr[u].l >= x && tr[u].r <= x) {
tr[u].minn = v;
}
else{
int mid = tr[u].l + tr[u].r >> 1;
if(x <= mid) modify(u<<1, x, v);
else if(x > mid) modify(u<<1|1, x, v);
pushup(u);
}
}
Node query(int u, int l, int r){
if(tr[u].l >= l && tr[u].r <= r) return tr[u];
else{
int mid = tr[u].l + tr[u].r >> 1;
if(l > mid) return query(u<<1|1, l, r);
else if(r <= mid) return query(u<<1, l, r);
else{
Node res;
Node L = query(u << 1, l, r), R = query(u << 1 | 1, l, r);
pushup(res, L, R);
return res;
}
}
}
int res[N];
int main(){
int n, m; cin >> n >> m;
memset(res, 0x3f, sizeof res);
for(int i = 1; i <= n; i ++){
int x; scanf("%d", &x);
a[i] = {x, i};
}
for(int i = 1; i <= m; i ++){
int l, r, x; scanf("%d %d %d", &l, &r, &x);
que[i] = {x, l, r, i};
}
sort(a + 1, a + 1 + n);
reverse(a + 1, a + 1 + n);
sort(que + 1, que + 1 + m);
reverse(que + 1, que + 1 + m);
build(1, 1, n);
int i, j;
for(i = 1, j = 1; i <= m + 1; i ++){
while(j <= n && que[j][0] > a[i][0]){
res[que[j][3]] = query(1, que[j][1], que[j][2]).minn;
j ++;
}
if(i == m + 1) break;
modify(1, a[i][1], a[i][0]);
}
for(int i = 1; i <= m; i ++){
if(res[i] != 0x3f3f3f3f) printf("%d\n", res[i]);
else printf("-1\n");
}
}
f[i][j]:表示前i个元素,分成j段的最小代价
f[i][j] = min(f[k][j - 1] + w(k + 1, i)) k = 1, 2, 3, 4,
这样来看时间复杂度起码是n^2*k
现在使用数据结构来进行优化
第一个维护枚举j,所以去寻找相邻两个j之间的转移是什么样的
当i变大的时候,前面的f[k][j - 1]是不变的,而后面的w(k + 1, i)变成w(k + 1, i + 1)增加的贡献是可以直接计算
并且上面增加的贡献是上一次a[j]出现的及其之前的w,所以这就是一个区间加法,然后查询f的值是一个区间减法
枚举每一个j的时候,我们上一层计算得到的f初始化线段树
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 2e5+10, mod = 1e9+7;
// 标签在当前节点已经发生作用了,但是还没有对儿孙发生作用
int f[N];
struct Node{
int l, r;
int minn, lazy;
}tr[4*N];
void pushup(Node &Fa, Node &L, Node &R){
Fa.minn = min(L.minn, R.minn);
}
void pushup(int u){
pushup(tr[u], tr[u<<1], tr[u<<1|1]);
}
void pushdown(Node &Fa, Node &L, Node &R){
// 标签融合
L.lazy += Fa.lazy;
R.lazy += Fa.lazy;
// 标签向下传递
L.minn += Fa.lazy;
R.minn += Fa.lazy;
// 标签清空
Fa.lazy = 0;
}
void pushdown(int u){
pushdown(tr[u], tr[u<<1], tr[u<<1|1]);
}
void build(int u, int l, int r){
if(l == r){
tr[u] = {l, r, f[l], 0};
}
else{
tr[u] = {l, r, 0, 0};
int mid = l + r >> 1;
build(u<<1, l, mid), build(u<<1|1, mid+1, r);
pushup(u);
}
}
void modify(int u, int l, int r, int v){
if(tr[u].l >= l && tr[u].r <= r) {
tr[u].lazy += v;
tr[u].minn += v;
}
else{
pushdown(u);
int mid = tr[u].l + tr[u].r >> 1;
if(l <= mid) modify(u<<1, l, r, v);
if(r > mid) modify(u<<1|1, l, r, v);
pushup(u);
}
}
int query(int u, int l, int r){
if(tr[u].l >= l && tr[u].r <= r) return tr[u].minn;
else{
pushdown(u);
int mid = tr[u].l + tr[u].r >> 1;
if(l > mid) return query(u<<1|1, l, r);
else if(r <= mid) return query(u<<1, l, r);
else return min( query(u<<1, l, r), query(u<<1|1, l, r) );
}
}
int a[N], pre[N], last[N];
int main(){
int n, k; scanf("%d %d", &n, &k);
for(int i = 1; i <= n; i ++){
scanf("%d", &a[i]);
last[i] = pre[a[i]];
pre[a[i]] = i;
}
for(int i = 1; i <= n; i ++) f[i] = (1 << 30);
for(int i = 1; i <= k; i ++){
build(1, 0, n);
for(int j = 1; j <= n; j ++){
// 注意这里需要last[j] - 1
if(last[j] != 0) modify(1, 0, last[j] - 1, j - last[j]);
// 因为维护的hj的值,所以这里到j-1就行了,当然写道j也没啥问题好像
f[j] = query(1, 0, j - 1);
}
}
cout << f[n] << endl;
}
// 对权值扫描,计算答案
#include<bits/stdc++.h>
#define left fdafadasd
#define right dasda
using namespace std;
const int N = 5e5+10;
typedef long long LL;
int left[N], right[N], l[N], r[N];
int pos[N];
int main(){
int n, k; scanf("%d %d", &n, &k);
for(int i = 1; i <= n; i ++){
int x; scanf("%d", &x);
pos[x] = i;
}
// 这里将左右边界设置成一个值非常地巧妙
for(int i = 0; i <= n + 1; i ++){
l[i] = max(0, i - 1);
r[i] = min(n + 1, i + 1);
}
int res = 0;
LL ans = 0;
for(int i = 1; i <= n; i ++){
int x = pos[i];
left[0] = x;
for(int j = 1; j <= k; j ++){
x = l[x];
left[j] = x;
}
x = pos[i];
right[0] = x;
for(int j = 1; j <= k; j ++){
x = r[x];
right[j] = x;
}
x = pos[i];
// 链表删除某一个节点
l[r[x]] = l[x];
r[l[x]] = r[x];
LL seg = 0;
// 这里左右移动边界的计算
for(int j = 1; j <= k; j ++){
seg += 1ll * (left[j - 1] - left[j]) * (right[k - j + 1] - right[k - j]);
}
ans += seg * i;
}
cout << ans << endl;
}
// 一共n次操作,所以序列的总长度是n
// a1 + a2 + a3 .... ak = n
// 所以ai的种类有根号n种
// 然后根据序列的长度再进行一次分块
// 长的序列维护原序列,短的序列维护sum
#include<bits/stdc++.h>
using namespace std;
const int N = 2e5+10;
typedef long long LL;
vector<int> p[N], large;
const int M = 500;
LL sum[M + 10][M + 10];
int main(){
int n, q; scanf("%d %d", &n, &q);
for(int i = 1; i <= q; i ++){
int op; scanf("%d", &op);
if(op == 1){
int x, y; scanf("%d %d", &x, &y);
int m = p[x].size();
if(m <= M){
for(int i = 0; i < m; i ++) sum[m][i] -= p[x][i];
}
p[x].push_back(y);
m ++;
if(m <= M){
for(int i = 0; i < m; i ++) sum[m][i] += p[x][i];
}
else if(m == M + 1) large.push_back(x);
}
else if(op == 2){
int z; scanf("%d", &z);
LL res = 0;
for(int i = 1; i <= M; i ++) res += sum[i][z % i];
for(auto big : large){
res += p[big][z % (int)p[big].size()];
}
printf("%lld\n", res);
}
}
return 0;
}