zoukankan      html  css  js  c++  java
  • sqrt数据结构

    简单题:弹飞绵羊

    分块:
    将序列分块,每块sqrt(n)个。
    在每个块中维护f[i],to[i]
    f[i] 表示跳几次可以跳出所在块
    to[i] 表示跳出所在块后到达的位置。
    在查询时,我们O(sqrt(n))的时间进行“整块”的模拟,可以得到结果。
    在修改i时,我们只需维护一下(l[belong[i]]--i)的序列就可以了。
    总复杂度:O(n*sqrt(n))

     1 #include<bits/stdc++.h>
     2 using namespace std;
     3 #define maxn 200002
     4 #define N 500 
     5 int to[maxn],f[maxn],n,R[500],L[500],val[maxn],pos[maxn];
     6 //能弹到哪儿,弹了几次 
     7 
     8 int query(int st)
     9 {
    10     int ans=0;
    11     while(st<=n)
    12     {
    13         ans+=f[st];
    14     //    cout<<f[st]<<endl;
    15         st=to[st];
    16     //    cout<<st<<endl;
    17     }
    18     return ans;
    19 }
    20 
    21 void change(int x)//对改的点所在的块暴力重构 
    22 {
    23     for(int j=R[x];j>=L[x];j--)//倒序循环因为修改了的点会对前面的点有影响 
    24     {
    25         if(j+val[j]<=R[x]) f[j]=f[j+val[j]]+1,to[j]=to[j+val[j]];
    26         else f[j]=1,to[j]=j+val[j];
    27     }
    28 }
    29 
    30 int main()
    31 {
    32     scanf("%d",&n);
    33     int bnum=(int)sqrt(n+0.5);
    34     for(int i=1;i<=bnum;i++)
    35     {
    36         L[i]=(i-1)*sqrt(n+0.5)+1;
    37         R[i]=i*sqrt(n+0.5);
    38     }
    39     if(R[bnum]<n) bnum++,L[bnum]=R[bnum-1]+1,R[bnum]=n;
    40     for(int i=1;i<=n;i++)
    41     scanf("%d",&val[i]);
    42     for(int i=1;i<=bnum;i++)
    43     {
    44         for(int j=R[i];j>=L[i];j--)//倒序!!! 
    45         {
    46             pos[j]=i;
    47             if(j+val[j]<=R[i]) {
    48                 to[j]=to[j+val[j]];
    49                 f[j]=f[j+val[j]]+1;
    50             }
    51             else f[j]=1,to[j]=j+val[j];
    52         }
    53     }//对于块把,每个要维护的信息进行块内的预处理 
    54     int m;
    55     scanf("%d",&m);
    56     for(int i=1;i<=m;i++)
    57     {
    58         int opt;
    59         scanf("%d",&opt);
    60         if(opt==1)
    61         {
    62             int st;
    63             scanf("%d",&st);
    64             printf("%d
    ",query(++st));
    65         }
    66         if(opt==2)
    67         {
    68             int j,k;
    69             scanf("%d%d",&j,&k);
    70             val[++j]=k;
    71             change(pos[j]);
    72         }
    73     } 
    74 }
    75 /*
    76 4 
    77 1 2 1 1 
    78 3
    79 1 1
    80 2 1 1
    81 1 1
    82 */
    View Code


    黑题:蒲公英(区间众数)

    https://www.cnblogs.com/acfunction/p/10051345.html

    整体思路:

    分块 考虑维护什么:
    对于整块 希望直接得到众数及其出现的次数  
    对于零散处 暴力求出每个数出现次数 但还要知道它们在整块中的出现次数 来更新答案
    维护两个要预处理的数组:ans表示块与块之间的众数是什么 sum表示某个数在某一块之前出现的次数
    O(n*sqrt(n))预处理
    细节:要离散化 用桶来记录比较出现次数 要记得还原

    ai1e9 ,先离散化。然后

    算法一:暴力 n^2,预计得分 20 ; 实际得分 20 (开了 O2 直接变成 85 啥操作)

    算法二:n≤40000 ,看来需要搬出分块大法。

    离散化部分:

    for(int i=1;i<=n;++i) cin>>a[i].w,a[i].num=i;
        sort(a+1,a+n+1,cmp1);
        for(int i=1;i<=n;++i){
            if(a[i].w!=a[i-1].w){
                s[++tot]=a[i].w;
                a[i].x=tot;
            }
            else a[i].x=a[i-1].x;
        }
    sort(a+1,a+n+1,cmp2);
    View Code

     然后对于求众数:

    我们考虑开个桶来存:

    int solve(int l,int r)
    {
        int p=pos[l],q=pos[r];
        if(p==q){//如果在同一个块中 就暴力for 
            int now=0;
            for(int i=l;i<=r;i++){
                t[num[i]]++;
                if(t[num[i]]>t[now]||(t[num[i]]==t[now]&&now>num[i])) now=num[i];
            }
            for(int i=l;i<=r;i++) t[num[i]]=0;//每次记得还原 
            return b[now];
        }
        int now=ans[p+1][q-1];
        t[now]+=sum[now][q-1]-sum[now][p];//初始化整块之间的众数及其出现次数 
        for(int i=l;i<=end[p];i++){//同样用桶去比较 
            if(!t[num[i]]) t[num[i]]+=sum[num[i]][q-1]-sum[num[i]][p];
            t[num[i]]++;
            if(t[num[i]]>t[now]||(t[num[i]]==t[now]&&now>num[i])) now=num[i];
        }
        for(int i=sta[q];i<=r;i++){
            if(!t[num[i]]) t[num[i]]+=sum[num[i]][q-1]-sum[num[i]][p];
            t[num[i]]++;
            if(t[num[i]]>t[now]||(t[num[i]]==t[now]&&now>num[i])) now=num[i];
        }//最后记得所有的清空 
        t[ans[p+1][q-1]]=0;
        for(int i=l;i<=end[p];i++) t[num[i]]=0;
        for(int i=sta[q];i<=r;i++) t[num[i]]=0;
        
        return b[now];
    }
    View Code


    历史研究(回滚莫队)  dalao:https://www.cnblogs.com/Parsnip/p/10969989.html#1745587478

    考虑如果用正常莫队的话我们是无法删除的,因为一旦删除了最大元素就无法找到次大元素

    对于回滚莫队:
    这名字真的取的有意思,一开始还不觉得,学了之后发现他真的在回滚.我们回滚莫队排序是按之前的莫队一样将询问  左块右位置 的来排序.

    回滚问题特别之处在哪里?
    他在滚.
    特别之处1
    我们对于左右端点都在同一块里的,直接暴力for 1遍统计答案,复杂度根号n.
    特别之处2
     我们是枚举每一块来统计答案的.此时我们只处理左端点在此块,右端点在右块的询问,左右同块的见1.我们把ql指针指向下一块的开头,qr指针指向这一块的末尾.对于此本块询问,我们先将qr指针向右移更新信息直到此询问的右端点.(当我们处理下一个询问的时候,qr只需要继续右移就可以了,因为询问左端点都同块的时候,我们是按右端点位置从小到大排序的,所以单调递增).
    这里好像没有回滚,并且由于qr单调递增,区间扩大,并没有删除操作.
    但是我还没讲ql.
    本块包含询问的左端点一定在ql左边(见ql定义).我们就将ql向左移更新答案(此时区间仍然在扩大,只有更新操作没有删除操作),到了询问的左端点,统计答案.
    然后就开始滚了,并且是回滚.ql向左到达询问左端点后,向右往初始位置(见ql定义)移,删除之前一路滚过来所更新的信息.然后ql回滚回来,qr仍不动,处理下个询问.
    所以就是 qr向右移动更新信息到询问右端点->ql向左滚 边滚边更新 至询问左端点,然后处理此区间答案,ql回滚删除之前一路滚来更新的信息,回到初位置->处理(下个询问->本块询问处理完->枚举下个块.
    回滚得十分形象.

     1 #include<cstdio>
     2 #include<algorithm>
     3 #include<cmath>
     4 #include<cstring>
     5 #define LL long long 
     6 using namespace std;
     7 const int MAXN = 1e5 + 10, INF = 1e9 + 7;
     8 inline int read() {
     9     char c = getchar(); int x = 0, f = 1;
    10     while(c < '0' || c > '9') {if(c == '-') f = -1; c = getchar();}
    11     while(c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar();
    12     return x * f;
    13 }
    14 int N, Q;
    15 LL out[MAXN], ans;
    16 int be[MAXN], date[MAXN], cnt[MAXN], a[MAXN], tot, base, num;
    17 struct Query {
    18     int l, r, id;
    19     bool operator < (const Query &rhs) const {
    20         if(be[l]==be[rhs.l]) return r<rhs.r;
    21         else return be[l]<be[rhs.l]; 
    22     }
    23 }q[MAXN];
    24 LL solve(int l, int r) {
    25     //每个数*出现次数的最大值
    26     //对块内,直接for 
    27     static int tim[MAXN]; 
    28     LL ans = 0;
    29     for(int i = l; i <= r; i++) 
    30     tim[a[i]] = 0;
    31     for(int i = l; i <= r; i++) 
    32     tim[a[i]]++, ans = max(ans, 1ll * tim[a[i]] * date[a[i]]);
    33     return ans;
    34 }
    35 void Add(int x) {//考虑加元素带来的影响 
    36     cnt[a[x]]++;
    37     ans = max(ans, 1ll * cnt[a[x]] * date[a[x]]);
    38 }
    39 void Del(int x) {
    40     cnt[a[x]]--;
    41 }
    42 int Get(int i, int id) {
    43     int R = min(N, id * base), ql = R + 1, qr = ql - 1; 
    44     ans = 0;
    45     memset(cnt, 0, sizeof(cnt));
    46     for(; be[q[i].l] == id; i++) {
    47         if(be[q[i].l] == be[q[i].r]) 
    48         {
    49             out[q[i].id] = solve(q[i].l, q[i].r); 
    50             continue;
    51         }
    52         while(qr < q[i].r) Add(++qr);//右端点从小到大排的序,不断往右走 
    53         LL cur = ans; 
    54         while(ql > q[i].l) Add(--ql);
    55         out[q[i].id] = ans;
    56         
    57         while(ql < R + 1) Del(ql++);//每次询问完之后重新统计答案,回滚的时候删除以前更新的信息 
    58         ans = cur;
    59     }
    60     return i;
    61 }
    62 main() {
    63     //freopen("4241.in", "r", stdin);
    64     //freopen("4241.out", "w", stdout);
    65     N = read(); Q = read(); base = sqrt(N);
    66     for(int i = 1; i <= N; i++) {
    67         a[i] = date[i] = read(); 
    68         be[i] = (i - 1) / base + 1;//每个所在的块的位置 
    69         num = max(num, be[i]);
    70     }
    71     
    72     sort(date + 1, date + N + 1);
    73     int tot = unique(date + 1, date + N + 1) - date - 1;//去重 
    74     //tot kinds of different numbers
    75     for(int i = 1; i <= N; i++) 
    76     a[i] = lower_bound(date + 1, date + N + 1, a[i]) - date;
    77     //离散化 
    78     for(int i = 1; i <= Q; i++) 
    79     q[i].l = read(), q[i].r = read(), q[i].id = i;
    80     sort(q + 1, q + Q + 1);
    81 
    82     for(int i = 1, id = 1; id <= num; id++)//对于每个块开始回滚 
    83     i = Get(i, id);
    84 
    85     for(int i = 1; i <= Q; i++)
    86     printf("%lld
    ", out[i]);
    87         
    88     return 0;
    89 }
    90 /*
    91 3 2
    92 2 3
    93 2
    94 3
    95 2
    96 2 3
    97 2
    98 3
    99 */
    回滚莫队


  • 相关阅读:
    云级Keyvalue数据库大比较
    如何诊断ORA125XX连接问题
    Oracle Internal Event:10235 Heap Checking诊断事件
    Oracle Database 11g R2 在Oracle Linux 6上获得认证
    一张图帮你搞懂oracle UGA User Global Area的概念
    Oracle Buffer Cache Chain图解
    ORA04036: 实例使用的 PGA 内存超出 PGA_AGGREGATE_LIMIT
    如何诊断ASMLIB故障
    Oracle RDBMS Server 11gR2 Preinstall RPM For Oracle Linux 6
    图解Oracle Buffer Cache LRU List
  • 原文地址:https://www.cnblogs.com/lkx422/p/11236575.html
Copyright © 2011-2022 走看看