[Violet]蒲公英 分块
发现写算法专题老是写不动,,,,
所以就先把我在luogu上的题解搬过来吧!
题目大意:查询区间众数,无修改,强制在线
乍一看是一道恐怖的题,仔细一看发现并没有那么难;
大致思路是这样的,首先我们要充分发挥分块暴力大法好的精神
先暴力预处理出每个块内每种蒲公英的个数,
然后求出对每个块而言的前缀和,
于是这样我们就可以区间查询任意两个块之间每种蒲公英的数量了
然后我们预处理出任意两个块之间的众数
最后对于每组询问,我们先找到夹在它们中间的块,
如果这个两个块r-l<=1,那么我们暴力求众数
为什么? 因为不这样的话,万一x,y在一个快,那么r可能会比l小,要特判
如果x,y隔得很近,同样有各种奇奇怪怪的情况要做特判,
那既然这么麻烦,我们不如直接暴力搞是吧。
如果两个块相差超过了1,那么我们先取出中间块的众数,作为我们的answer,然后对旁边两个块暴力处理众数(此处注意判断时要加上中间的蒲公英)。
最后我们就得到了答案,
但是注意到ai的范围很大,所以我们需要离散化。
并且由于数量相同时要优先编号小的,于是我们处理众数的时候要多加这个判断
基本就是这样了。。。
表示本蒟蒻一A过了还是很开心的(^▽^)(虽然说第一次交没删调试结果too many or too few lines 了,但是去掉调试就过了,也可以算是一A嘛是吧)
下面代码:
1 #include<bits/stdc++.h> 2 using namespace std; 3 #define R register int 4 #define AC 40100 5 #define ac 210 6 #define D printf("line in %d\n",__LINE__); 7 int block,n,m,answer,tot; 8 int s[AC]; 9 struct abc{ 10 int num,w,x; 11 }b[AC];//原数列+离散化后数组 12 int sum[ac][AC];//每种数字块内前缀和 13 int ans[ac][ac];//任意两块之间的众数 14 int belong[AC];//所属块 15 int color[AC]; 16 17 inline int read() 18 { 19 int x=0;char c=getchar(); 20 while(c>'9' || c<'0') c=getchar(); 21 while(c>='0' && c<='9') x=x*10+c-'0',c=getchar(); 22 return x; 23 } 24 25 bool cmp1(abc a,abc b) 26 { 27 return a.w < b.w; 28 } 29 30 bool cmp2(abc a,abc b) 31 { 32 return a.num < b.num; 33 } 34 35 void search(int x,int y) 36 { 37 // printf("%d %d\n",x,y); 38 int l=x/block + 1,r=y/block - 1;//取出中间块 39 if(r - l <= 1)//如果x,y相差很小,那么暴力统计 40 { 41 answer=0; 42 for(R i=x;i<=y;i++) 43 if((++color[b[i].x] > color[answer]) || (color[b[i].x] == color[answer] && b[i].x < answer)) answer=b[i].x; 44 for(R i=x;i<=y;i++) 45 --color[b[i].x]; 46 printf("%d\n",s[answer]); 47 return ; 48 } 49 else//不然的话 50 { 51 int ll=l * block - 1,rr=(r+1) * block; 52 answer=ans[l][r]; 53 for(R i=x;i<=ll;i++) 54 { 55 ++color[b[i].x]; 56 if(color[b[i].x] + sum[r][b[i].x] - sum[l-1][b[i].x] > color[answer] + sum[r][answer] - sum[l-1][answer]) answer=b[i].x; 57 else if(color[b[i].x] + sum[r][b[i].x] - sum[l-1][b[i].x] == color[answer] + sum[r][answer] - sum[l-1][answer] && b[i].x < answer) answer=b[i].x;//编号小也要优先,因为一行写不下,为了美观,,,就用else吧,不然就用||了 58 } 59 for(R i=rr;i<=y;i++) 60 { 61 ++color[b[i].x]; 62 if(color[b[i].x] + sum[r][b[i].x] - sum[l-1][b[i].x] > color[answer] + sum[r][answer] - sum[l-1][answer]) answer=b[i].x; 63 else if(color[b[i].x] + sum[r][b[i].x] - sum[l-1][b[i].x] == color[answer] + sum[r][answer] - sum[l-1][answer] && b[i].x < answer) answer=b[i].x; 64 } 65 for(R i=x;i<=ll;i++) --color[b[i].x]; 66 for(R i=rr;i<=y;i++) --color[b[i].x]; 67 printf("%d\n",s[answer]); 68 return ; 69 } 70 } 71 72 void pre()//读入 73 { 74 n=read(),m=read(); 75 block=sqrt(n); 76 for(R i=1;i<=n;i++) b[i].w=read(),b[i].num=i; 77 sort(b+1,b+n+1,cmp1); 78 for(R i=1;i<=n;i++) 79 { 80 if(b[i].w != b[i-1].w) 81 { 82 s[++tot]=b[i].w;//存下对应新编号的对应真实编号 83 b[i].x=tot; 84 } 85 else b[i].x=b[i-1].x;//离散化 86 } 87 sort(b+1,b+n+1,cmp2); 88 } 89 90 void getsum() 91 {//注意0也被分在块0中 92 for(R i=1;i<=n;i++) 93 { 94 belong[i]=i/block; 95 sum[belong[i]][b[i].x]++; 96 } 97 for(R i=0;i<=belong[n];i++) 98 for(R j=1;j<=tot;j++) 99 sum[i][j]+=sum[i-1][j]; 100 } 101 102 void getans() 103 { 104 for(R i=0;i<=belong[n];i++) 105 { 106 int be=i * block,now=0; 107 if(!be) be=1;//这里和作诗不同,因为这里的now要参与比较了,而不是单纯的统计,而now初始值为0,所以color[0]不能被修改 108 for(R j=be;j<=n;j++) 109 { 110 if((++color[b[j].x] > color[now]) || (color[b[j].x] == color[now] && b[j].x < now)) now=b[j].x;//更新ans 111 ans[i][belong[j]]=now;//存下新ans 112 } 113 for(R j=be;j<=n;j++) --color[b[j].x];//暴力撤销 114 } 115 /*for(R i=0;i<=belong[n];i++) 116 { 117 for(R j=i;j<=belong[n];j++) 118 printf("%d ",ans[i][j]); 119 printf("\n"); 120 }*/ 121 } 122 123 void work()//预处理出前缀和和众数 124 { 125 int a,b; 126 for(R i=1;i<=m;i++) 127 { 128 a=(read() + s[answer] -1) % n + 1,b=(read() + s[answer] - 1) % n + 1;//获取询问 129 if(a < b) search(a,b); 130 else search(b,a);//因为经过了运算,所以大小顺序就可能改变了 131 } 132 } 133 134 int main() 135 { 136 // freopen("in.in","r",stdin); 137 pre(); 138 getsum(); 139 getans(); 140 work(); 141 // fclose(stdin); 142 return 0; 143 }