嘘~ 正在从服务器偷取页面 . . .

CF354D Transferring Pyramid 题解


CF354D Transferring Pyramid 题解

题目链接:CF354D Transferring Pyramid

题意

cxy 正在使用一个有趣的数据结构——金字塔。

金字塔由 $n$ 行组成,其中第 $i$ 行有 $i$ 个格子组成。每一行的最左端相对于上一行左移了半个格子的宽度,金字塔上的格子由 $1$ 到 $\binom {n+1}{2}$,从上至下,从左至右的编号。

下图是一个 $n = 5$ 的例子:

这个神奇的数据结构能接受下列两种操作:

  1. 改变一个特定位置上的值。它需要用 $3$ 个数作为指令:1 i v。其中 $i$ 为格子的编号,$v$ 为改变后的值。
  2. 改变一个子金字塔的值。上图蓝色部分显示的是以 $5$ 号格子为顶的子金字塔,它需要用 $s + 2$ 个数作为指令:2 i v1 v2 ... vs,其中 $i$ 为子金字塔顶的格子的编号,$s$ 为子金字塔的大小 (如上图中是 $6$),$v_i$ 为对应修改后的值。(可以不改变,即 $v_i$ 为之前的值)

正式地,一个以 $i$ 行 $j$ 列为顶的子金字塔会包含行 $i \sim n$,其中第 $(i + p)$ 行包含格子 $j \sim (j + p)\quad(0 \leq p \leq n - i)$。

现在有两个金字塔,金字塔 $A$ 的 $k$ 个格子已经有非 $0$ 的数,而金字塔 $B$ 的所有格子都是 $0$。cxy 需要寻找一个操作序列,使得当它施加在 $B$ 上后,能使 $B$ 中的所有格子与 $A$ 中的对应格子均相等。

在她的计算机中,执行一条指令的时钟周期数等于该操作序列的长度。因此,在众多合法操作序列中,她希望找到一条长度最短的操作序列(时钟周期最短的),请你帮她求出操作序列的长度的最小可能值。

输入格式

第一行包含两个正整数 $n, k$ $(n, k \leq 10^5)$ ,分别表示金字塔的行数与非 $0$ 数的个数。

接下来的 $k$ 行,每行包含两个正整数,第 $i + 1$ 行的整数 $r_i, c_i (1 \leq c_i \leq r_i \leq n)$ 描述其中一个非 $0$ 数所在的格子的行数和列数,保证同一个格子不会被描述超过一遍。

输出格式

输出一行一个整数,表示操作序列的长度的最小可能值。

首先我们先把它转成方便维护的形式

容易发现,原来的 $r_i,c_i$ 对应的 $x_i,y_i$ 满足

同时可以发现,原来的赋值就变成了一个类似直角三角形的赋值

设 $f_{i,j}(0 \le j \le i \le n)$ 表示正准备使用操作2粉刷第 $i$ 列高度为 $j$ 的金字塔,它上面类似梯形的区域的最小花费

例如下图中的黄色部分,就是当前准备使用操作2粉刷的金字塔

而上面的蓝色部分就是 $f_{i,j}$

则最终答案就是 $f_{n,0}$ 。

记第 $i$ 列高度($y$ 坐标)大于等于 $j$ 的 $i-j+1$ 个格子中,有 $s_{i,j}$ 个非 $0$ 格,则

注意:这里是 $f_{i,0}$ ,因为这个状态比较麻烦,单独提出来讲。

yhx巨佬太强了1其实这个方程并不是那么显然就能推出来的(

因为时间有限,这里就不画图说明了,仅作描述

  • $f_{i-1,0} + 3s_{i,1}$ 表示「 $1$ 到 $i-1$ 构成的完整三角形的花费」加上「填涂大于等于 $j$ 的 $i-j+1$ 个格子的花费」

  • $\min\limits_{1 \le k \le i}$是枚举从 $i-1$ 的哪个地方转移,这个比较好懂吧(

  • 先解释后面的 $3_{i,k+1}$ ,这个指的是 $i$ 上面的那 $i-j+1$ 个格子的花费

  • $\left(\dfrac{1}{2}k^2 + \dfrac{1}{2}k + 2\right)$ 比较麻烦,建议画一画图(我时间有限,对不住了

    这个指的是除了 $f_{i-1,k-1}$ 和 $i$ 上面那一段,剩下的一个小三角形的花费,其实原来的柿子是 $\frac{k(k+1)}{2} + 2$ 。

终于解释完了 $j=0$ 的情况。

对于 $j \ge 1$ 的情况,有

这个就很显然了,不解释了。

说了半天,这还只是一个 $O(n^2)$ 的暴力

考虑继续优化,当然这需要善于观察性质。

注意到其实很多时候粉刷整个大三角形是很浪费的

全部单点修改的花费也只有 $3k$

因此我们可以发现,判断一个三角形是否有必要粉刷

或者具体地, $f_{i,j}$ 在 $j$ 大于某个边界时,没有必要转移了,一定是不优的

高度为 $h$ 的三角形,粉刷所需的花费为 $\dfrac{1}{2}h^2 + \dfrac{1}{2}h + 2$

则有不等式,$\dfrac{1}{2}h^2 + \dfrac{1}{2}h + 2 > 3k$ ,即 $h < O(\sqrt{6k})$

因此第二维只要枚举到 $O(\sqrt{6k})$ 就好了

时间复杂度 $O(n \sqrt{k})$ ,然后 $f_{i,j}$ 滚动一下就能过了

代码:

#include <iostream>
#include <string>
#include <vector>
#include <algorithm>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cstdarg>
#include <cmath>
#include <iomanip>
#include <random>
using namespace std;
#define int long long
#define INF 0x3f3f3f3f3f3f3f3f
// #define INF 0x3f3f3f3f
namespace FastIO
{
    #define gc() readchar()
    #define pc(a) putchar(a)
    #define SIZ (int)(1e6+15)
    char buf1[SIZ],*p1,*p2;
    char readchar()
    {
        if(p1==p2)p1=buf1,p2=buf1+fread(buf1,1,SIZ,stdin);
        return p1==p2?EOF:*p1++;
    }
    template<typename T>void read(T &k)
    {
        char ch=gc();T x=0,f=1;
        while(!isdigit(ch)){if(ch=='-')f=-1;ch=gc();}
        while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=gc();}
        k=x*f;
    }
    template<typename T>void write(T k)
    {
        if(k<0){k=-k;pc('-');}
        static T stk[66];T top=0;
        do{stk[top++]=k%10,k/=10;}while(k);
        while(top){pc(stk[--top]+'0');}
    }
}using namespace FastIO;
#define N ((int)(1e5+15))
#define H ((int)(888))
#define F first
#define S second

pair<int,int> p[N];
int n,k,h;
int f[2][N],s[N],pre,now;
void down(int &x,int y){ x = (x > y) ? y : x;}
signed main()
{
    ios::sync_with_stdio(0);
    cin.tie(0);cout.tie(0);
    // freopen("check.in","r",stdin);
    // freopen("check.out","w",stdout);
    read(n); read(k);
    h=min(n,(int)sqrt(6*k));
    for(int i=1,r,c; i<=k; i++)
    {
        read(r); read(c);
        p[i]={n-r+c,n-r+1};
    }
    sort(p+1,p+1+k); memset(f,0x3f,sizeof(f));
    int x=1,y; f[0][0]=0;
    for(int i=1; i<=n; i++)
    {
        y = min(i,h); now = i & 1; pre = now ^ 1;
        for(int i=0; i<=y+1; i++) f[now][i]=INF,s[i]=0;
        for(; x<=k && p[x].F==i; x++) ++s[p[x].S > y ? y+1 : p[x].S];
        for(int j=y; j>=0; j--) s[j] += s[j+1];
        f[now][0] = f[pre][0] + s[1] * 3;
        for(int j=1; j<=y; j++)
            down(f[now][0], f[pre][j-1] + ((j*(j+1)>>1) + 2 + s[j+1]*3));
        for(int j=1; j<=y; j++)
            f[now][j]=f[now][j-1],down(f[now][j], f[pre][j-1] + s[j+1]*3);
    }
    write(f[n&1][0]); pc('\n');
    return 0;
}

参考文献

1. https://yhx-12243.github.io/OI-transit/records/cf354D.html

文章作者: q779
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-ND 4.0 许可协议。转载请注明来源 q779 !
评论
  目录