BZOJ2724: [Violet 6]蒲公英

n<=40000个数,在线问m<=50000次区间众数,数字Ai<=1e9。

重要结论:$mode(a cup  b)epsilon mode(a) cup b$,显然。

用分块做,对区间[L,R]取众数,就先对他们跨过的块O(1)取答案--预处理A(i,j)表示块i到块j的众数即可,然后对两边零散的数和他们跨过的块的那个众数都算一次在[L,R]的出现次数即可。

现在问题就是算某个数在某个区间出现次数。

方法一:把每个数的位置取出来二分!n*sqrt(n)*logn,慢

方法二:前缀思想!计算[1,R]的减去[1,L-1]的,问题再次转化。然后[1,R]的某个数的出现次数,可以先预处理B(i,j)表示前i个块数j出现次数,然后C(i,j,x)表示块i前j个数中数x出现多少次,这里要在每个块里再离散化一次,D(i,j)表示数i在块j的C数组中的x是多少,因为x太大C开不下。。

其实C和D是多余的,前后两段数直接开桶记录然后用B来算中间就行了。

 1 #include<stdio.h>
 2 #include<algorithm>
 3 #include<string.h>
 4 #include<stdlib.h>
 5 #include<math.h>
 6 //#include<bitset>
 7 //#include<iostream>
 8 using namespace std;
 9 
10 int n,m,q;
11 #define maxn 41011
12 #define maxm 311
13 int lisan[maxn],li=0,bud[maxn];
14 int most[maxm][maxm],num[maxm],lnum,s[maxm][maxn],cnt[maxm][maxm][maxm],pos[maxn][maxm],bel[maxn],tot,a[maxn];
15 
16 int calc(int p,int v) {if (!p) return 0;return s[bel[p]-1][v]+cnt[bel[p]][p-(bel[p]-1)*m][pos[v][bel[p]]];}
17 int main()
18 {
19     scanf("%d%d",&n,&q);
20     m=(int)sqrt(n);
21     for (int i=1;i<=n;i++) bel[i]=(i-1)/m+1;
22     tot=bel[n];
23     for (int i=1;i<=n;i++) scanf("%d",&a[i]),lisan[++li]=a[i];
24     sort(lisan+1,lisan+1+li); li=unique(lisan+1,lisan+1+li)-lisan-1;
25     for (int i=1;i<=n;i++) a[i]=lower_bound(lisan+1,lisan+1+li,a[i])-lisan;
26     for (int i=1,now=1;i<=n;i+=m,now++)
27     {
28         memset(bud,0,sizeof(bud));
29         int Max=0;
30         for (int j=i;j<=n;j++)
31         {
32             bud[a[j]]++;
33             if (bud[a[j]]>bud[Max] || (bud[a[j]]==bud[Max] && a[j]<Max)) Max=a[j];
34             if (bel[j]!=bel[j+1]) most[now][bel[j]]=Max;
35         }
36     }
37     for (int i=1,now=1;i<=n;i+=m,now++)
38     {
39         lnum=0;
40         for (int j=i;bel[j]==bel[i];j++) num[++lnum]=a[j];
41         sort(num+1,num+1+lnum); lnum=unique(num+1,num+1+lnum)-num-1;
42         for (int j=i;bel[j]==bel[i];j++)
43         {
44             for (int k=i;k<j;k++) cnt[now][j-i+1][pos[a[k]][now]]=cnt[now][j-i][pos[a[k]][now]];
45             pos[a[j]][now]=lower_bound(num+1,num+1+lnum,a[j])-num,
46             cnt[now][j-i+1][pos[a[j]][now]]++;
47         }
48         for (int j=1,r=min(now*m,n)-(now-1)*m;j<=li;j++)
49             s[now][j]=s[now-1][j]+cnt[now][r][pos[j][now]];
50     }
51     int x,y,last=0;
52     memset(bud,0,sizeof(bud));
53     while (q--)
54     {
55         scanf("%d%d",&x,&y);
56         x=(x+last-1)%n+1,y=(y+last-1)%n+1;
57         if (x>y) {int t=x;x=y;y=t;}
58         if (bel[x]==bel[y])
59         {
60             int Max=0;
61             for (int i=x;i<=y;i++)
62             {
63                 bud[a[i]]++;
64                 if (bud[a[i]]>bud[Max] || (bud[a[i]]==bud[Max] && a[i]<Max)) Max=a[i];
65             }
66             printf("%d
",(last=lisan[Max]));
67             for (int i=x;i<=y;i++) bud[a[i]]--;
68         }
69         else
70         {
71             int Max=most[bel[x]+1][bel[y]-1],tot=calc(y,Max)-calc(x-1,Max);
72             for (int j=x,tmp;bel[j]==bel[x];j++)
73             {
74                 tmp=calc(y,a[j])-calc(x-1,a[j]);
75                 if (tmp>tot || (tmp==tot && a[j]<Max)) Max=a[j],tot=tmp;
76             }
77             for (int j=y,tmp;bel[j]==bel[y];j--)
78             {
79                 tmp=calc(y,a[j])-calc(x-1,a[j]);
80                 if (tmp>tot || (tmp==tot && a[j]<Max)) Max=a[j],tot=tmp;
81             }
82             printf("%d
",(last=lisan[Max]));
83         }
84     }
85     return 0;
86 }
View Code
原文地址:https://www.cnblogs.com/Blue233333/p/8035085.html