But my words, like|

MessageBoxA

园龄:4年10个月粉丝:4关注:0

2024-11-06 09:25阅读: 30评论: 0推荐: 0

QOJ5173 染色

涉及知识点:扫描线,贪心

前言

非常好的一道题,转化十分精彩,之前完全没想到能有接近线性的做法。

题意

Link

有一串纸条,上有 n 个格子,每个格子最开始有某种初始颜色,你可以用一秒钟将某个格子染成任意颜色,或者向左向右移动,前提是移动前后的格子颜色相同。有 q 组询问,每次询问从 uv 的最短时间,询问之间独立。

n,q106

思路

转化

正推没有什么头绪,我们可以反过来想,移动 len 距离最多可能需要的时间为 2len2,其中 len1 是移动操作,另外 len1 是染色操作,表示每一次移动前都将当前格子染成下一个格子的颜色,不难用发现这样的策略,无论格子颜色如何都一定能够成功抵达终点。于是在这个基础上考虑如何使步数更小,我们发现如果其中有一对 x<y 满足 colx=coly,此时总步数就可以减小 1,我们可以将 [x+1,y1] 的格子都染为 colx 然后直接移动,这样从 x 移动到 y 就只需要染色 yx1 次,但按照我们之前的策略会染 yx 次。

ABCA

之前的策略:

ABCA BBCA BBCA BCCA BCCA BCAA BCAA(共 6 步)

优化策略:

ABCA AACA AAAA AAAA AAAA AAAA(共 5 步)

子问题:拆子区间

因此我们的问题就转化为了:给定一段区间,求区间里最多能拆出多少子区间,满足子区间两端点颜色相同,并且子区间之间不交(端点可交)。

这听起来似乎有点经典,事实上,如果没有多次询问的限制,这是一个简单的线性 DP:

线性 DP:

f[i]=max(max{f[j]},max{f[k]+1}) (j,k<i,col[k]=col[i])

g[x]col[k]=x 时最大的 f[k]+1maxf 为全局最大的 f[j] 即可简单优化为线性。

for(int i=1;i<=n;i++){
    f[i]=max(maxf,g[a[i]]);
    maxf=max(maxf,f[i]);
    g[a[i]]=max(g[a[i]],f[i]+1);
}

眼看 DP 是没什么办法优化下去了……但是我们发现,这题可以直接贪心!

怎么贪呢?对于一个点 x,它的下一个区间选择它右侧区间中右端点最小的区间(这个区间的左端点可以为 x,也可以大于 x,但不能小于 x),将这个区间的右端点记为 fa[x]。不断从起点跳 fa[x],直至再跳就要超过终点为止,跳的步数就是最大子区间数。

比如说,1,2,2,3,2,3,5,1fa 数组为 3,3,5,6,1,1,1,1

为什么这样贪心对呢?因为 fa[x] 的设定相当于我们固定住了左边界 x,于是当然是右边界越靠左越优。

fa 是可以线性求的,只需要先预处理出每个格子下一个与它颜色相同格子的编号 nxt,注意此处 i 是倒序遍历:

fa[i]=min(fa[i+1],nxt[i])

转移方程表示我们每遍历到一个格子 i 就用区间 [i,nxt[i]] 来更新一下答案。

离线询问

最后需要解决的问题,就是怎么处理这么多组询问。我们可以扫描线,当前扫到 r,利用并查集维护某个点 l 能跳到的最大的 r 的点,于是一组 [l,r] 的询问的答案即为 l 跳多少次能跳到并查集的根,在求 fa 时顺便用一个类似后缀和的东西维护一下即可,具体可以看代码的 suf 数组。

细节

还有一个小细节,原问题并未保证起点 u 终点 v,不过根据我们以上分析,显然 uvvu 答案是一样的,直接交换起点终点就可以了。

代码

#include<bits/stdc++.h>
using namespace std;
template<class T>inline void rd(T &x){
    T res=0,f=1;
    char ch=getchar();
    while(!isdigit(ch)){if(ch=='-')f=-1; ch=getchar();}
    while(isdigit(ch)){res=res*10+ch-'0';ch=getchar();}
    x=res*f;
}
template<class T>inline void wt(T x,char endch='\0'){
    static char wtbuff[20];
    static int wtptr;
    if(x==0){
        putchar('0');
    }
    else{
        if(x<0){x=-x;putchar('-');}
        wtptr=0;
        while(x){wtbuff[wtptr++]=x%10+'0';x/=10;}
        while(wtptr--) putchar(wtbuff[wtptr]);
    }
    if(endch!='\0') putchar(endch);
}
typedef pair<int,int> pii;
const int MAXN=1e6+5;
int n,q,a[MAXN],ans[MAXN];
int fa[MAXN],suf[MAXN],buck[MAXN],nxt[MAXN];
vector<int>son[MAXN];
struct QUERY{
    int l,r,id;
    QUERY(){}
    QUERY(int a,int b,int c):l(a),r(b),id(c){}
};
vector<QUERY>qry[MAXN];
struct DSU{
    int fa[MAXN];
    int find(int x){
        if(fa[x]==x) return x;
        else return fa[x]=find(fa[x]);
    }
    void init(int len){
        for(int i=1;i<=len;i++){
            fa[i]=i;
        }
    }
}dsu;
int main(){
    rd(n);rd(q);
    for(int i=1;i<=n;i++){
        rd(a[i]);
    }
    int x,y;
    for(int i=1;i<=q;i++){
        rd(x);rd(y);
        if(x>y) swap(x,y);
        qry[y].emplace_back(x,y,i);
    }

    dsu.init(n+1);
    for(int i=n;i>=1;i--){
        if(!buck[a[i]]) nxt[i]=n+1;
        else nxt[i]=buck[a[i]];
        buck[a[i]]=i;
    }
    fa[n+1]=n+1;suf[n+1]=-1;
    for(int i=n;i>=1;i--){
        fa[i]=min(fa[i+1],nxt[i]);
        son[fa[i]].push_back(i);
        suf[i]=suf[fa[i]]+1;
    }
    for(int i=1;i<=n;i++){
        for(auto it:son[i]){
            dsu.fa[dsu.find(it)]=i;
        }
        for(auto it:qry[i]){
            ans[it.id]=2*(it.r-it.l+1)-2-(suf[it.l]-suf[dsu.find(it.l)]);
        }
    }

    for(int i=1;i<=q;i++){
        wt(ans[i],'\n');
    }
    return 0;
}

本文作者:MessageBoxA

本文链接:https://www.cnblogs.com/SkyNet-PKN/p/18528336

版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。

posted @   MessageBoxA  阅读(30)  评论(0编辑  收藏  举报
历史上的今天:
2023-11-06 AGC027E ABBreviate
点击右上角即可分享
微信分享提示
评论
收藏
关注
推荐
深色
回顶
收起
  1. 1 evening Corn Wave
  2. 2 Группа крови Кино
  3. 3 The Sound Of Silence Simon & Garfunkel
  4. 4 dB doll YUE.STEVEN
Группа крови - Кино
00:00 / 00:00
An audio error has occurred, player will skip forward in 2 seconds.