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 */
    回滚莫队


  • 相关阅读:
    解决SharePoint 文档库itemadded eventhandler导致的上传完成后,编辑页面保持报错的问题,错误信息为“该文档已经被编辑过 the file has been modified by...”
    解决SharePoint 2013 designer workflow 在发布的报错“负载平衡没有设置”The workflow files were saved but cannot be run.
    随机实例,随机值
    Spring4笔记
    struts2笔记(3)
    struts2笔记(2)
    获取文本的编码类型(from logparse)
    FileUtil(from logparser)
    DateUtil(SimpleDateFormat)
    struts2笔记
  • 原文地址:https://www.cnblogs.com/lkx422/p/11236575.html
Copyright © 2011-2022 走看看