关于(个人认为的)正解分块被卡 T 一个点那些事

P4168 [Violet] 蒲公英

Zack_zhu @ 2021-06-26 13:37:26

[题目链接](https://www.luogu.com.cn/problem/P4168) ```cpp #include <cstdio> #include <iostream> #include <cstring> #include <algorithm> #include <cmath> using namespace std; const int MAXN = 4e4+5; const int MAXQ = 505; int k[MAXQ][MAXQ],s[MAXQ][MAXN],a[MAXN],lsh[MAXN],l[MAXQ],r[MAXQ],t[MAXN],p[MAXN],pos[MAXN],maxx,now,cnt,n,m,x,y,ans,block; template <typename T> inline void read(T &s) { s = 0; bool w = false; char ch = getchar(); while(ch < '0'||ch > '9') { if(ch == '-') w = true; ch = getchar(); } while(ch >= '0'&&ch <= '9') { s = (s<<3)+(s<<1)+(ch^48); ch = getchar(); } s = w == true ? -s:s; return; } inline void in_block() { memset(t,0,sizeof(t)); maxx = 0; for(int i = x;i <= y;i++)//暴力扫 { t[a[i]]++; if(t[maxx] < t[a[i]]) maxx = a[i]; if(t[maxx] == t[a[i]]) maxx = min(maxx,a[i]); } ans = p[maxx]; return; } inline void out_block() { memset(t,0,sizeof(t)); maxx = 0; for(int i = x;i <= r[pos[x]];i++)//暴力查整块左边的 { t[a[i]]++; if(t[maxx] < t[a[i]]) maxx = a[i]; if(t[maxx] == t[a[i]]) maxx = min(maxx,a[i]); } for(int i = l[pos[y]];i <= y;i++)//暴力查整块右边的 { t[a[i]]++; if(t[maxx] < t[a[i]]) maxx = a[i]; if(t[maxx] == t[a[i]]) maxx = min(maxx,a[i]); } if(maxx == k[pos[x] + 1][pos[y] - 1])//如果相等,直接还原并返回(BTW:p为离散化前对应的值) ans = p[maxx]; else { t[maxx] += s[pos[y] - 1][maxx] - s[pos[x]][maxx];//加上块内的边上求得的众数 int ans2 = t[k[pos[x] + 1][pos[y] - 1]] + s[pos[y] - 1][k[pos[x] + 1][pos[y] - 1]] - s[pos[x]][k[pos[x] + 1][pos[y] - 1]];//块内众数的个数 if(t[maxx] > ans2)//选择答案 ans = p[maxx]; else if(t[maxx] == ans2) ans = p[min(maxx,k[pos[x] + 1][pos[y] - 1])]; else ans = p[k[pos[x] + 1][pos[y] - 1]]; } return; } int main() { read(n); read(m); block = sqrt(n); for(int i = 1;i <= n;i++) { read(a[i]); lsh[i] = a[i]; } sort(lsh+1,lsh+1+n);//离散化 cnt = unique(lsh+1,lsh+1+n) - lsh - 1; for(int i = 1;i <= n;i++) now = a[i],a[i] = lower_bound(lsh+1,lsh+1+cnt,a[i]) - lsh,p[a[i]] = now; for(int i = 1;i <= block;i++)//预处理左右端点 { l[i] = (i - 1) * block + 1; r[i] = i * block; } if(r[block] < n) { l[++block] = r[block - 1] + 1; r[block] = n; } for(int i = 1;i <= block;i++) for(int j = l[i];j <= r[i];j++) pos[j] = i;//每一个位置对应的块的编号 for(int i = 1;i <= block;i++) { for(int j = 1;j <= cnt;j++) s[i][j] += s[i-1][j];//sum数组处理 1 ~ i中j的个数 for(int j = l[i];j <= r[i];j++) s[i][a[j]]++; } for(int i = 1;i <= block;i++) { memset(t,0,sizeof(t));//开桶记录出现了几次。。。 for(int j = i;j <= block;j++) { k[i][j] = k[i][j-1];//k为在块 i ~ j中的众数 for(int z = l[j];z <= r[j];z++) { t[a[z]]++; if(t[k[i][j]] < t[a[z]]) k[i][j] = a[z]; if(t[k[i][j]] == t[a[z]]) k[i][j] = min(k[i][j],a[z]);//暴力预处理 } } } ans = 0; for(int i = 1;i <= m;i++) { read(x); read(y); x = (x + ans - 1) % n + 1; y = (y + ans - 1) % n + 1;//还原左右区间 if(x > y) swap(x,y); if(pos[x] - pos[y] <= 1) in_block();//在同一块内暴力处理 else out_block();//不同块分块处理 printf("%d\n",ans); } return 0; } ```

by Zack_zhu @ 2021-06-26 16:02:16

@听取MLE声一片 好吧,谢谢巨佬。。。


by Zack_zhu @ 2021-06-26 16:03:20

主要是调了三天人都调傻了(无语)


by Katyusha_qwq @ 2021-06-26 16:05:44

不能直接memset吧,不然时间复杂度会退化到O(n^2)


by Katyusha_qwq @ 2021-06-26 16:12:38

应该每次\sqrt{n}遍历散块撤销桶


by Katyusha_qwq @ 2021-06-26 16:13:06

@我被JC了


by Zack_zhu @ 2021-06-26 16:21:14

@I_CE_IOI 是这样吗
但 T 得更离谱了啊


by FutaRimeWoawaSete @ 2021-06-26 19:16:53

@我被JC了 你先把每类数离散化后按位置在 vector 里面存放,然后在暴力边角块的时候就可以在 vector 里面看现在这个值能不能在区间内装下超过最长的众数个数次个数,然后可以的话就继续往里面跳。

由于你边角块为 2 \sqrt n 个数,而你也处理了中间块的答案,所以用现在这个答案跳的话肯定是从后面那个边角块开始指的值,也就是说答案至多跳 \sqrt n 次,这样就可以保证时间复杂度了,你原来的代码我不清楚具体是哪里爆了,不过你预处理和查询的常数太大了,这么改常数会小很多。

给你放一下伪代码,这个是yuno3求区间众数出现次数的伪代码其实大同小异。

int Qry(int l,int r)
{
    int LL = pos[l] , RR = pos[r];
    if(LL == RR){int maxn = 0;for(int i = l ; i <= r ; i ++) bucket[a[i]] = 0;for(int i = l ; i <= r ; i ++){bucket[a[i]] ++ ; maxn = max(maxn , bucket[a[i]]);}return maxn;}   
    int Ans = F[LL + 1][RR - 1];
    for(int i = l ; i <= R[LL] ; i ++){int lst = v[a[i]].size();while(pto[i] + Ans < lst && v[a[i]][pto[i] + Ans] <= r) Ans ++;}
    for(int i = L[RR] ; i <= r ; i ++) while(pto[i] - Ans >= 0 && v[a[i]][pto[i] - Ans] >= l) Ans ++; 
    return Ans;
} 

by Katyusha_qwq @ 2021-06-29 12:20:32

@我被JC了

if(pos[x] - pos[y] <= 1)
       in_block();//在同一块内暴力处理 
 else
       out_block();//不同块分块处理 

应该是pos[y]-pos[x]<=1吧 不然每次都是暴力O(n)


by dying @ 2021-07-17 15:15:18

反正我O(n^2)暴力能过


上一页 |