bzoj 1780

这是一道环上的问题,我们先将一个环展开,再复制一次。

这样,任何一个合法方案一定对应在转换后的序列的一些连续的区间,使得它们的并的长度大于等于圈长。

然后,我们将区间合并一下(就是将一些被其他区间包含的区间去掉)。

假设某个答案的区间是r1,r2,r3,...rk,我们可以让ri为"与ri-1连接的右端点最靠右的区间“,明显这样不会不原来的答案劣。

所以,一旦确定了起点,那么在该起点的情况下最优的覆盖圆环的方案就确定了,确定了区间,我们可以用倍增的思想来判断一串连续区间覆盖len最少需要多少个,(有点像跳跃式的LCA求法中用倍增的思想)。这个O(logn)可搞,然后枚举起点是O(n),总的复杂度是O(nlogn)。

/**************************************************************
    Problem: 1780
    User: idy002
    Language: C++
    Result: Accepted
    Time:2124 ms
    Memory:33796 kb
****************************************************************/
 
#include <cstdio>
#include <vector>
#include <algorithm>
#define fprintf(...)
#define maxn 200010
#define maxp 18
using namespace std;
 
struct Rng {
    int lf, rg;
    Rng(){}
    Rng( int lf, int rg ):lf(lf),rg(rg){}
    bool operator<( const Rng &b ) const { return lf<b.lf || (lf==b.lf&&rg>b.rg); }
};
 
int len, n;
Rng rng[maxn]; int tot;
vector<int> stk;
bool mark[maxn];
int nxt[maxn][maxp+1], dis[maxn][maxp+1];
 
int main() {
    scanf( "%d%d", &len, &n );
    for( int i=1,lf,ln; i<=n; i++ ) {
        scanf( "%d%d", &lf, &ln );
        rng[++tot] = Rng(lf,lf+ln);
    }
    for( int i=1,otot=tot; i<=otot; i++ ) 
        rng[++tot] = Rng( rng[i].lf+len, rng[i].rg+len );
    sort( rng+1, rng+1+tot );
    for( int i=1; i<=tot; i++ ) 
        if( !mark[i] ) {
            stk.push_back( i );
            fprintf( stderr, "Got [%d,%d]
", rng[i].lf, rng[i].rg );
            for( int j=i+1; j<=tot; j++ )
                if( rng[j].rg<=rng[i].rg ) mark[j]=true;
                else break;
        }
    for( int t=0; t<stk.size(); t++ ) {
        int u=stk[t];
        nxt[u][0] = u;
        dis[u][0] = 0;
        for( int tt=t+1; tt<stk.size(); tt++ ) {
            int v=stk[tt];
            if( rng[v].lf<=rng[u].rg ) {
                nxt[u][0] = v;
                dis[u][0] = rng[v].rg-rng[u].rg;
            } else break;
        }
    }
    for( int p=1; p<=maxp; p++ ) 
        for( int t=0; t<stk.size(); t++ ) {
            int u=stk[t];
            nxt[u][p] = nxt[nxt[u][p-1]][p-1];
            dis[u][p] = dis[u][p-1]+dis[nxt[u][p-1]][p-1];
        }
    /*
    for( int t=0; t<stk.size(); t++ ) {
        int u=stk[t];
        fprintf( stderr, "from [%d,%d]: 
", rng[u].lf, rng[u].rg );
        for( int p=0; p<=5; p++ )
            fprintf( stderr, "nxt[%d]=[%d,%d] dis[%d]=%d
", p, rng[nxt[u][p]].lf, rng[nxt[u][p]].rg, p, dis[u][p] );
        fprintf( stderr, "
" );
    }
    */
    int ans=n;
    for( int t=0; t<stk.size(); t++ ) {
        int u=stk[t];
        int remain=len-(rng[u].rg-rng[u].lf);
        int tans = 1;
        for( int p=maxp; dis[u][0] && dis[u][0]<remain; p-- )
            if( dis[u][p]<remain ) {
                remain-=dis[u][p];
                tans += 1<<p;
                u=nxt[u][p];
            }
        tans++;
        if( dis[u][0]==0 ) continue;
        fprintf( stderr, "from [%d,%d] got ans %d
", rng[u].lf, rng[u].rg, tans );
        if( tans<ans ) ans=tans;
    }
    printf( "%d
", ans );
}
View Code
原文地址:https://www.cnblogs.com/idy002/p/4344190.html