• 洛谷P4155 BZOJP4444 [SCOI2015]国旗计划


    自己做出来的第一道倍增(fake)
    原题链接
    看到题目,贪心水题!不仅思维难度低,代码也(难)写,三下五除二就写了出来,过了样例。但是一交,只有40pts。一道紫题能让我快速水40pts,良心出题人啊>ω<!
    然后我们考虑如何优化这个贪心:
    首先我们发现,对于每一个人,在贪心的思路下,他的下一个最优的接替者都是一定的。这表明我们之前(O(n^2))的贪心中,有很多步骤都是不必要的,如果我们能记录一下从某个人开始,经过几轮交替后的接替者是谁就好了。
    于是我们令(f[i][j])表示从第(i)个人开始,经过(j)次交替后的接替者,但很尴尬的是,这样会爆空间,时间复杂度貌似也不好。然后我们想到了倍增:
    (f[i][j])表示从第(i)个人开始,经过(2^j)次交替后的接替者,(f[i][j])可以从(f[f[i][j-1]][j-1])转移过来,查询的时候我们就像倍增求(LCA)一样,从大到小往上跳,直到当前接替者的奔袭区间的右端点距离(i)的左端点不小于(m)就行了。
    还有一个要注意的地方,我们在预处理(f[i][0])时,不能用(O(n))扫一遍。考虑一种极端情况:所有奔袭区间都为定长(m-1),且左端点依次相差(1),如果我们还扫一遍的话就会被卡回(O(n^2))了。因为奔袭区间没有互相包含,所以左端点不超过当前区间右端点,且离当前区间右端点最近的区间一定是下一个最优选择,我们二分一下左端点就行了。
    上代码(倍增题的预处理都巨长,是我写丑了嘛):

    #include <cstdio>
    #include <algorithm>
    
    using namespace std;
    
    #define N (int)2e5
    #define re register
    #define il inline
    
    int n, m, l, r, ans[N+5], f[4*N+5][22], power[30]; //注意断环为链
    
    struct Seg {
        int id, l, r;
        friend bool operator < (const Seg &lhs, const Seg &rhs) {
            return lhs.l < rhs.l;
        }
    }seg[4*N+5];
    
    il int read() {
        int s = 0;
        char c = getchar();
        while(c < '0' || c > '9') c = getchar();
        while(c >= '0' && c <= '9') s = s*10+c-'0', c = getchar();
        return s;
    }
    
    il void write(int x) {
        if(x > 9) write(x/10);
        putchar(x%10+'0');
    }
    
    il void init() {
        n = read(), m = read();
        power[0] = 1;
        for(re int i = 1; i <= n; ++i) power[i] = (power[i-1]<<1); //预处理2的次幂
        for(re int i = 1; i <= n; ++i) {
            seg[i].id = seg[i+n].id = i;
            seg[i].l = read(), seg[i].r = read();
            if(seg[i].l > seg[i].r) seg[i].r += m;
            seg[i+n].l = seg[i].l+m, seg[i+n].r = seg[i].r+m;
        }
        sort(seg+1, seg+2*n+1);
        for(re int i = 1, l, r, mid; i <= n; ++i) {
            l = i+1, r = 2*n;
            while(l != r) { //二分左端点
            	mid = (l+r)/2;
            	if(seg[mid].l > seg[i].r) r = mid;
            	else l = mid+1;
            }
            f[i][0] = l-1, f[i+n][0] = l-1+n; //初值
        }
        for(re int j = 1; power[j] <= n; ++j)
            for(re int i = 1; i <= n; ++i)
                f[i][j] = f[f[i][j-1]][j-1], f[i+n][j] = f[i][j]+n; //递推f数组
    }
    
    int main() {
        init();
        for(re int i = 1, cnt, lim, u; i <= n; ++i) {
            cnt = 1, lim = seg[i].l+m, u = i;
            for(re int j = 20; j >= 0; --j)
                if(seg[f[u][j]].id && seg[f[u][j]].r < lim) cnt += power[j], u = f[u][j];
            ans[seg[i].id] = cnt+1;
        }
        for(re int i = 1; i <= n-1; ++i) write(ans[i]), putchar(' ');
        write(ans[n]);
        return 0;
    }
    
  • 相关阅读:
    处理MVC中默认的Json方法返回时间的问题
    Linq To DataSet
    (C#)利用Aspose.Cells组件导入导出excel文件
    泛型转带逗号分割的字符串
    request参数集合绑定实体实现defaultmodebinder
    .NET反射
    用过属性来给标签加样式
    Servlet中的过滤器Filter用法
    JQueryUI确认框 confirm
    Openwrt自定义CGI实现
  • 原文地址:https://www.cnblogs.com/dummyummy/p/9596457.html
Copyright © 2020-2023  润新知