
bzoj3489: A simple rmq problem
f[i][j] 加入这个数。因为是从大到小扫的,因此可以保证值都是最大的,然后当一个块加入够数的时候,就不加了。 这个过程显然可以用一个并查集维护,维护下一个还可以加数的块在哪里 这样的话,复杂度就是 nn√ 了,感觉还是很优秀哒 出题人的贴吧大家也可以看一看 然而我用的是分块。。设B为块的大小 我们就预处理f[i][j]表示第i块到第j块的一个表,储存2*B+1个只出现了一次的数。。 至于为什么是2*B+1,那是因为零散的地方最多就是2*B,也就是有2*B个出现一次的数可能会GG,这时候我们要保证要是有解的话一定要找出来。。 然后到时候就把整块的拿出来,然后用零散的更新一下他就可以了。。要注意的是他们也能作为答案,更新一下就好了。。 至于怎么求f[i][j],我用了个傻逼的方法,多了个log。。然而居然过了。。可能比较难卡然后我正在想怎么优化的时候,他居然过了。。 还和FYC的可持久化树套树差不多速度,都是很慢。。 于是优化就没想了,留坑。。已填 时间复杂度 O(nn√)
发布日期:2021-05-06 23:48:00
浏览次数:22
分类:精选文章
本文共 3098 字,大约阅读时间需要 10 分钟。
update2018.1.3
今天晚上,陈队问我是怎么做的,我说分块
才想起有一个log没有去掉 于是想了一下,还真的可以去掉这个log 我们先对数集进行离散化 考虑从后往前扫,一次加入数字 然后加入一个数的时候,更新一下这个数出现的最后两个位置 那么出现了一次的就是这两个点之间 然后我们当遇到块的端点的时候,然后就从大到小扫一下数组,设这两个位置为 l,r 然后我们就对在 l,r 区间里面的块端点 j ,题解
网上有很多乱七八糟的方法
有KDtree,树套树等等一大堆。。#include#include #include #include #include #include #include using namespace std;const int N=100005;const int NN=320;int n,m,nn;int a[N];int cnt,L[NN],R[NN],belong[N]; int mymin (int x,int y){ return x y?x:y;}void prepare() { L[1]=1;belong[1]=1;cnt=1; for (int u=1;u<=n;u++) { belong[u]=cnt; if (u%nn==0) { R[cnt]=u; cnt++; L[cnt]=u+1; } } cnt=belong[n];R[cnt]=n;}int f[NN][NN][2*NN];//第i块到第j块的表int c[N];//这个数字出现了多少次set ooo;set ::iterator it;int next[N],last[N];int vis[N],shen;//这个数字有没有用过bool cmp (int x,int y){ return x>y;}inline int read(){ int x=0,f=1;char ch=getchar(); while(ch<'0'||ch>'9'){ if(ch=='-')f=-1;ch=getchar();} while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();} return x*f;}void prepare1 ()//预处理f数组 和last和next数组{ memset(vis,127,sizeof(vis)); for (int u=n;u>=1;u--) { next[u]=vis[a[u]]; vis[a[u]]=u; } memset(vis,-1,sizeof(vis)); for (int u=1;u<=n;u++) { last[u]=vis[a[u]]; vis[a[u]]=u; } for (int u=1;u<=cnt;u++) { ooo.clear(); memset(c,0,sizeof(c)); for (int i=L[u];i<=n;i++) c[a[i]]++;//这个数又出现了一次 for (int i=n;i>=1;i--) if (c[i]==1)//这个数刚好出现了一次 ooo.insert(i); for (int i=n;i>=R[u];i--)//扫回去 { if (i==R[belong[i]])//如果已经到了一个块的右端点,那么到他下一个块的答案就可以统计了 { int o=belong[i]+1;//下一个块 /*printf("%d %d %d\n",u,i,c[9]); system("pause");*/ if (o<=cnt) { for (set ::reverse_iterator it=ooo.rbegin();it!=ooo.rend();it++) { f[u][o][0]++; f[u][o][f[u][o][0]]=(*it); if (f[u][o][0]>nn*2) break; } } } c[a[i]]--; if (c[a[i]]==1) ooo.insert(a[i]);//先把他弄进来,以后再踢掉他,所以这里的话会有2*sqrtn个数 if (c[a[i]]==0) ooo.erase(a[i]); } }}void Ins (){ n=read();m=read();nn=sqrt(n); for (int u=1;u<=n;u++) a[u]=read();} int solve1 (int l,int r)//这一个的答案 { int ans=0; if (belong[l]==belong[r])//如果在同一个块中暴力搞就好了 { for (int u=l;u<=r;u++) if (last[u] r) ans=mymax(ans,a[u]); return ans; } shen++; int x=belong[l],y=belong[r]; for (int u=l;u<=R[x];u++) { if (last[u] r) ans=mymax(ans,a[u]); vis[a[u]]=shen; } for (int u=L[y];u<=r;u++) { if (last[u] r) ans=mymax(ans,a[u]); vis[a[u]]=shen; } x++; for (int u=1;u<=f[x][y][0];u++) if (vis[f[x][y][u]]!=shen) { ans=mymax(ans,f[x][y][u]); break; } return ans;}void solve (){ memset(vis,0,sizeof(vis));shen=0; int lastans=0; while (m--) { int l,r; l=read();r=read(); l=(l+lastans)%n+1;r=(r+lastans)%n+1; if (l>r) swap(l,r); lastans=solve1(l,r); printf("%d\n",lastans); }}int main(){ Ins(); prepare(); prepare1(); solve(); return 0;}
发表评论
最新留言
关注你微信了!
[***.104.42.241]2025年04月08日 00时18分21秒
关于作者

喝酒易醉,品茶养心,人生如梦,品茶悟道,何以解忧?唯有杜康!
-- 愿君每日到此一游!
推荐文章
matlab文件管理
2021-05-08
Printer Queue UVA - 12100
2021-05-08
看过这篇剖析,你还不懂 Go sync.Map 吗?
2021-05-08
Go入门(1)——数据类型
2021-05-08
到底什么是短链接
2021-05-08
Volatile 原理和使用场景解析
2021-05-08
Linux 分区磁盘占满,导致 ssh 登陆闪退
2021-05-08
【并发编程】实现多线程的几种方式
2021-05-08
Spring系列.@EnableRedisHttpSession原理简析
2021-05-08
设计模式系列博客传送门
2021-05-08
设计模式——访问者模式
2021-05-08
同步锁 —— ReentrantReadWriteLock
2021-05-08
Nginx简介
2021-05-08
Nginx的Gzip功能
2021-05-08
当我们开发一个接口时需要注意些什么
2021-05-08
chrome 截长图功能
2021-05-08