zoukankan      html  css  js  c++  java
  • bzoj 2038 小z的袜子 莫队例题

    莫队,利用可以快速地通过一个问题的答案得到另一问题的答案这一特性,合理地组织问题的求解顺序,将已解决的问题帮助解决当前问题,来优化时间复杂度。

    典型用法:处理静态(无修改)离线区间查询问题。

    线段树也是处理区间问题的一个有力工具,它和莫队算法各有特点:

    线段树可以支持修改,并且单次操作时间复杂度一般为O(log),支持在线,但是要求可以进行快速的区间合并操作,两个区间如不能快速合并(f(n)*O(log)>O(n)),则用线段树就没有什么实际价值了(暴力都比它块)

    莫队算法可以解决某些线段树不能解决的静态离线问题,但它要求可以快速地从一个答案得到另一个答案。

    对于区间问题,假如我们得到了区间[l,r]的答案,能通过它用O(1)的时间得到[l-1,r],[l+1,r],[l,r-1],[l,r+1]的答案,那么我们将[l,r]看成二维平面上的点,两个点的距离用哈密顿距离表示,一个不错的想法是找到图的最小生成树,然后暴力推出一个点,其它点从它延伸过去就行了,时间复杂度是距离和加上暴力的那个点花的时间。

    这道题除了上面的做法,还可以分块,如果分成n^0.5块,可以做到O(n^1.5)的复杂度。

    做法是先将原颜色序列分成根号n块,然后将询问先按左端点排序,对于每一块的询问再按右端点排序(都是升序)。

    每次计算一个左端点在一个块中的询问,先暴力这个区间的第一个询问,然后后面的每个询问从它前一个询问推(具体看代码)

    时间复杂度可以这么看:

    排序O(nlogn)

    对于每个询问,它从前一个推过来,因为它们在同一块中,前端点改变最多O(n^0.5)次,有O(n)个询问,所以前端点变化O(n*n^0.5)次

    对于每一段,后端点变化是O(n)的,而最多有O(n^0.5)段,所以后端点变化O(n^0.5*n)次

    所以从一个[l,r]推到它四个相邻的点的次数是O(n^1.5),而转移是O(1)的,所以总的复杂度是O(n^1.5)。

    不论是分块还是最小生成树,都是想用最少的转移和最少的暴力将所有询问解决。

    不会写最小生成树的做法。这个是分块,感谢proverbs

      1 /**************************************************************
      2     Problem: 2038
      3     User: idy002
      4     Language: C++
      5     Result: Accepted
      6     Time:836 ms
      7     Memory:2972 kb
      8 ****************************************************************/
      9  
     10 #include <cstdio>
     11 #include <cmath>
     12 #include <algorithm>
     13 #define maxn 50010
     14 using namespace std;
     15  
     16 typedef long long lng;
     17  
     18 lng gcd( lng a, lng b ) {
     19     return b ? gcd(b,a%b) : a;
     20 }
     21 struct Query {
     22     int l, r, idx;
     23     lng ans[2];
     24     Query(){}
     25     Query( int l, int r, int idx ) : l(l), r(r), idx(idx) {}
     26     void set( lng sum ) {
     27         lng len = (r-l+1);
     28         lng a = sum, b = len*(len-1)/2;
     29         lng c = gcd(a,b);
     30         if( c ) {
     31             ans[0] = a/c;
     32             ans[1] = b/c;
     33         } else {
     34             ans[0] = 0;
     35             ans[1] = 1;
     36         }
     37     }
     38 };
     39 bool cmpl( const Query & a, const Query & b ) {
     40     return a.l < b.l;
     41 }
     42 bool cmpr( const Query & a, const Query & b ) {
     43     return a.r < b.r;
     44 }
     45 bool cmpid( const Query & a, const Query & b ) {
     46     return a.idx < b.idx;
     47 }
     48 struct Range {
     49     int l, r;
     50     Range(){}
     51     Range( int l, int r ) : l(l), r(r) {}
     52 };
     53  
     54 int n, m;
     55 int clr[maxn], cnt[maxn];
     56 int tot;
     57 Range rng[maxn];
     58 Query qry[maxn];
     59  
     60 void partition() {
     61     int len = (int)ceil(sqrt(n));
     62     tot = n/len;
     63     for( int i=1; i<=tot; i++ ) {
     64         rng[i].l = rng[i-1].r+1;
     65         rng[i].r = rng[i-1].r+len;
     66     }
     67     if( rng[tot].r < n ) {
     68         tot++;
     69         rng[tot].l = rng[tot-1].r+1;
     70         rng[tot].r = n;
     71     }
     72 }
     73  
     74 void work() {
     75     sort( qry+1, qry+1+m, cmpl );
     76     int s, t; 
     77     lng sum;
     78     s = t = 1;
     79     for( int i=1; i<=tot; i++ ) {
     80         while( s<=m && qry[s].l<rng[i].l ) s++;
     81         while( t<=m && qry[t].l<=rng[i].r ) t++;
     82         if( s>m || qry[s].l>rng[i].r ) continue;
     83         sort( qry+s, qry+t, cmpr );
     84         sum = 0;
     85         for( int j=qry[s].l; j<=qry[s].r; j++ ) 
     86             sum += cnt[clr[j]]++;
     87         qry[s].set( sum );
     88         for( int q=s+1; q<t; q++ ) {
     89             if( qry[q].l > qry[q-1].r ) {    //  ( ) [ ]
     90                 for( int j=qry[q-1].l; j<=qry[q-1].r; j++ )
     91                     cnt[clr[j]]--;
     92                 sum = 0;
     93                 for( int j=qry[q].l; j<=qry[q].r; j++ )
     94                     sum += cnt[clr[j]]++;
     95             } else if( qry[q].l <= qry[q-1].l ) {    //  [ ( ) ]
     96                 for( int j=qry[q].l; j<qry[q-1].l; j++ )
     97                     sum += cnt[clr[j]]++;
     98                 for( int j=qry[q-1].r+1; j<=qry[q].r; j++ )
     99                     sum += cnt[clr[j]]++;
    100             } else {    //  ( [ ) ]
    101                 for( int j=qry[q-1].l; j<qry[q].l; j++ ) 
    102                     sum -= --cnt[clr[j]];
    103                 for( int j=qry[q-1].r+1; j<=qry[q].r; j++ )
    104                     sum += cnt[clr[j]]++;
    105             }
    106             qry[q].set( sum );
    107         }
    108         for( int j=qry[t-1].l; j<=qry[t-1].r; j++ )
    109             cnt[clr[j]]--;
    110     }
    111 }
    112  
    113 int main() {
    114     scanf( "%d%d", &n, &m );
    115     for( int i=1; i<=n; i++ ) scanf( "%d", clr+i );
    116     for( int i=1,l,r; i<=m; i++ ) {
    117         scanf( "%d%d", &l, &r );
    118         qry[i] = Query( l, r, i );
    119     }
    120     partition();
    121     work();
    122     sort( qry+1, qry+1+m, cmpid );
    123     for( int i=1; i<=m; i++ ) 
    124         printf( "%lld/%lld
    ", qry[i].ans[0], qry[i].ans[1] );
    125 }
    View Code

    nbut 1457:

    询问一个区间中每种颜色的数量的立方和

      1 #include <cstdio>
      2 #include <cstring>
      3 #include <cmath>
      4 #include <algorithm>
      5 #define maxn 100010
      6 using namespace std;
      7 
      8 typedef long long lng;
      9 
     10 struct Qu {
     11     int l, r, id;
     12 };
     13 bool cmpl( const Qu & a, const Qu & b ) {
     14     return a.l < b.l;
     15 }
     16 bool cmpr( const Qu & a, const Qu & b ) {
     17     return a.r < b.r;
     18 }
     19 
     20 
     21 int n, m;
     22 int idx[maxn], itot;
     23 int clr[maxn]; 
     24 lng cnt[maxn];
     25 int lx[maxn], rx[maxn], stot;
     26 Qu qu[maxn];
     27 lng ans[maxn];
     28 
     29 
     30 void partition() {
     31     int len = (int)ceil(sqrt(n));
     32     stot = n/len;
     33     rx[0] = 0;
     34     for( int i=1; i<=stot; i++ ) {
     35         lx[i] = rx[i-1]+1;
     36         rx[i] = rx[i-1]+len;
     37     }
     38     if( rx[stot]!=n ) {
     39         stot++;
     40         lx[stot] = rx[stot-1]+1;
     41         rx[stot] = n;
     42     }
     43 }
     44 void makeid() {
     45     sort( idx+1, idx+1+n );
     46     int tot = unique( idx+1, idx+1+n ) - idx;
     47     for( int i=1; i<=n; i++ ) 
     48         clr[i] = lower_bound( idx+1, idx+tot, clr[i] ) - idx;
     49 }
     50 lng cube( lng a ) {
     51     return a*a*a;
     52 }
     53 void update( lng &sum, int c, int delta ) {
     54     sum -= cube(cnt[c]);
     55     cnt[c] += delta;
     56     sum += cube(cnt[c]);
     57 }
     58 void work() {
     59     sort( qu+1, qu+1+m, cmpl );
     60     for( int i=1,s=1,t=1; i<=stot; i++ ) {
     61         memset( cnt, 0, sizeof(cnt) );
     62         while( s<=m && qu[s].l<lx[i] ) s++;
     63         while( t<=m && qu[t].l<=rx[i] ) t++;
     64         sort( qu+s, qu+t, cmpr );
     65         
     66         lng sum = 0;
     67         for( int j=qu[s].l; j<=qu[s].r; j++ ) 
     68             update( sum, clr[j], +1 );
     69         ans[qu[s].id] = sum;
     70         for( int q=s+1; q<t; q++ ) {
     71             if( qu[q].l<=qu[q-1].l ) {
     72                 //    [ ( ) ]
     73                 for( int j=qu[q].l; j<qu[q-1].l; j++ ) 
     74                     update( sum, clr[j], +1 );
     75                 for( int j=qu[q-1].r+1; j<=qu[q].r; j++ )
     76                     update( sum, clr[j], +1 );
     77             } else if( qu[q].l>qu[q-1].r ) {
     78                 //    ( ) [ ]
     79                 for( int j=qu[q-1].l; j<=qu[q-1].r; j++ )
     80                     cnt[clr[j]]--;
     81                 sum = 0;
     82                 for( int j=qu[q].l; j<=qu[q].r; j++ )
     83                     update( sum, clr[j], +1 );
     84             } else {
     85                 //    ( [ ) ]
     86                 for( int j=qu[q-1].l; j<qu[q].l; j++ )
     87                     update( sum, clr[j], -1 );
     88                 for( int j=qu[q-1].r+1; j<=qu[q].r; j++ )
     89                     update( sum, clr[j], +1 );
     90             }
     91             ans[qu[q].id] = sum;
     92         }
     93     }
     94 }
     95 int main() {
     96     scanf( "%d", &n );
     97     for( int i=1; i<=n; i++ ) {
     98         scanf( "%d", idx+i );
     99         clr[i] = idx[i];
    100     }
    101     scanf( "%d", &m );
    102     for( int i=1; i<=m; i++ ) {
    103         scanf( "%d%d", &qu[i].l, &qu[i].r );
    104         qu[i].id = i;
    105     }
    106     makeid();
    107     partition();
    108     work();
    109     for( int i=1; i<=m; i++ )
    110         printf( "%lld
    ", ans[i] );
    111 }
    View Code
  • 相关阅读:
    一个简单的knockout.js 和easyui的绑定
    knockoutjs + easyui.treegrid 可编辑的自定义绑定插件
    Knockout自定义绑定my97datepicker
    去除小数后多余的0
    Windows Azure Web Site (15) 取消Azure Web Site默认的IIS ARR
    Azure ARM (1) UI初探
    Azure Redis Cache (3) 创建和使用P级别的Redis Cache
    Windows Azure HandBook (7) 基于Azure Web App的企业官网改造
    Windows Azure Storage (23) 计算Azure VHD实际使用容量
    Windows Azure Virtual Network (11) 创建VNet-to-VNet的连接
  • 原文地址:https://www.cnblogs.com/idy002/p/4297478.html
Copyright © 2011-2022 走看看