一个假莫队算法总结 (bzoj2021 小z的袜子 bzoj1878 HH的项链 bzoj2120 数颜色)
2018-01-24 10:24
531 查看
这里强烈推荐我学莫队的 美妙的 大米饼的blog:
https://www.cnblogs.com/Paul-Guderian/p/6933799.html
正如大米饼所言
若谈及入门,那么BZOJ2038的美妙袜子一题堪称顶尖。
求出在某一段区间种能拿到相同颜色的袜子的最大值
区间不太分散
修改比较少
不要求在线
那就
美妙的暴力——莫队算法
通过尽量减少左右区间跳的次数
通过旁边的区间快速地求出当前区间的值
一道water 题:
求某一段区间中的种数
那么第一反应就是 : 大型数据结构走起
恭喜你 和我一样呢 [握个爪爪]
于是某谷上的题解出现了一堆
诸如 离线+树状数组 主席树 %%%%%%
看数据 修改很少 范围也很小
那150行的大型数据结构我还是选择50行的暴力把[捂脸]
带修莫队:
假想你有一台时光机 可以跳到不同的时间~
加多一个时间time 即是第几次修改之后的询问
当左右区间都一样的时候 看tim
然后接着一通乱跳就好
缺一个树上莫队……有空再来写把T T
https://www.cnblogs.com/Paul-Guderian/p/6933799.html
bzoj 2021 小z的袜子
http://www.lydsy.com/JudgeOnline/problem.php?id=2038正如大米饼所言
若谈及入门,那么BZOJ2038的美妙袜子一题堪称顶尖。
求出在某一段区间种能拿到相同颜色的袜子的最大值
区间不太分散
修改比较少
不要求在线
那就
美妙的暴力——莫队算法
通过尽量减少左右区间跳的次数
通过旁边的区间快速地求出当前区间的值
#include <cstdio> #include <cstring> #include <algorithm> #include <cmath> using namespace std; struct node { long long l,r,id; long long n,m; } tr[51000]; long long s[51000],pos[51000],ans;//s:区间中第i个颜色的袜子有多少只 pos:分块用 int c[51000];//color int n,m; long long sqr(int x){return x*x;} int cmp(node x,node y)//排序 如果左端点在不在同一个块中,按左端点排 否则按右端点排 { if (pos[x.l]==pos[y.l]) return x.r<y.r; else return x.l<y.l; } int cmp_id(node x,node y) { return x.id<y.id; } long long gcd(long long x,long long y) { if (y==0) return x; else return gcd(y,x%y); } void change(int i,int k) //增加/减少k只第i个位置的袜子 { ans-=sqr(s[c[i]]); s[c[i]]+=k; ans+=sqr(s[c[i]]); } int main() { scanf("%d%d",&n,&m); for (int i=1; i<=n; i++) scanf("%d",&c[i]); //分块 int N=sqrt(n); for (int i=1;i<=n;i++) { pos[i]=(i-1)/N+1; } for (int i=1; i<=m; i++) { scanf("%lld%lld",&tr[i].l,&tr[i].r); tr[i].id=i; } sort(tr+1,tr+1+m,cmp); int l=1,r=0; for (int i=1; i<=m; i++) { while (l<tr[i].l){change(l,-1);l++;} while (l>tr[i].l){l--;change(l,1);} while (r<tr[i].r){r++;change(r,1);} while (r>tr[i].r){change(r,-1);r--;} /* 先推一波公式[手动滑稽] 某种颜色选两只/所有方案 ∑(s[i]*(s[i]-1))/ n*(n-1) =∑(s[i]^2 -s[i]) /n*(n-1) =∑s[i]^2 - n / n*(n-1) */ if (l==r) { tr[i].n=0; tr[i].m=0; continue; } tr[i].n=ans-(tr[i].r-tr[i].l+1); tr[i].m=(tr[i].r-tr[i].l+1)*(tr[i].r-tr[i].l+1-1); int x=gcd(tr[i].n,tr[i].m); tr[i].n/=x; tr[i].m/=x; } sort(t 4000 r+1,tr+1+m,cmp_id); for (int i=1;i<=m;i++) printf("%lld/%lld\n",tr[i].n,tr[i].m); return 0; }
一道water 题:
bzoj1878 HH的项链
http://www.lydsy.com/JudgeOnline/problem.php?id=1878求某一段区间中的种数
那么第一反应就是 : 大型数据结构走起
恭喜你 和我一样呢 [握个爪爪]
于是某谷上的题解出现了一堆
诸如 离线+树状数组 主席树 %%%%%%
看数据 修改很少 范围也很小
那150行的大型数据结构我还是选择50行的暴力把[捂脸]
#include <cstdio> #include <cstring> #include <algorithm> #include <cmath> using namespace std; struct node { int l,r,id,ans; }tr[210000]; int s[1100000],pos[51000],col[51000],ans; int cmp(node x,node y) { if (pos[x.l]==pos[y.l]) return x.r<y.r; else return x.l<y.l; } int cmp_id(node x,node y) { return x.id<y.id; } void change(int i,int k) { s[col[i]]+=k; if (s[col[i]]==0&&k==-1) ans--; if (s[col[i]]==1&&k==1) ans++; } int main() { int n,m; scanf("%d",&n); for (int i=1;i<=n;i++){scanf("%d",&col[i]);} int N=sqrt(n); for (int i=1;i<=n;i++) pos[i]=(i-1)/N+1; scanf("%d",&m); for (int i=1;i<=m;i++) { scanf("%d%d",&tr[i].l,&tr[i].r); tr[i].id=i; } sort(tr+1,tr+1+m,cmp); int l=1,r=0; for (int i=1;i<=m;i++) { while (l<tr[i].l) {change(l,-1);l++;} while (l>tr[i].l) {l--;change(l,1);} while (r<tr[i].r) {r++;change(r,1);} while (r>tr[i].r) {change(r,-1);r--;} tr[i].ans=ans; } sort(tr+1,tr+1+m,cmp_id); for (int i=1;i<=m;i++) printf("%d\n",tr[i].ans); return 0; }
带修莫队:
bzoj 2120 数颜色
emm……假想你有一台时光机 可以跳到不同的时间~
加多一个时间time 即是第几次修改之后的询问
当左右区间都一样的时候 看tim
然后接着一通乱跳就好
#include <cstdio> #include <cstring> #include <algorithm> #include <cmath> using namespace std; struct node1 { int l,r,id,tim,ans; }tr[210000]; struct node2 { int x,y,ip; //把原来是颜色为x的改为颜色为y 位置为ip }c[1100]; int s[1100000],pos[11000],col[11000],now[11000],ans; bool cmp(node1 x,node1 y)//左右区间同块看修改 { if (pos[x.l]!=pos[y.l]) return x.l<y.l; else if (pos[x.r]!=pos[y.r]) return x.r<y.r; else return x.tim<y.tim; } bool cmp_id(node1 x,node1 y) { return x.id<y.id; } void change(int i,int k) { s[col[i]]+=k; if (s[col[i]]==0&&k==-1) ans--; if (s[col[i]]==1&&k==1) ans++; } int l=1,r=0,tim=0; void tchange(int x,int y)//时间修改 把位置为x 的颜色改为y { if(l<=x&&x<=r) //当这个修改在区间中 即会影响ans 就要change一波 { change(x,-1); col[x]=y; change(x,1); } else col[x]=y; } int main() { int n,m,na=0,nc=0; //na:询问次数(ask) nc:改变次数(change) char st[5]; scanf("%d",&n); scanf("%d",&m); for (int i=1;i<=n;i++){scanf("%d",&col[i]);now[i]=col[i];} //一如既往的分块 int N=sqrt(n); for (int i=1;i<=n;i++) pos[i]=(i-1)/N+1; for (int i=1;i<=m;i++) { int x,y; scanf("%s",st+1); scanf("%d%d",&x,&y); if (st[1]=='Q') {tr[++na].l=x;tr[na].r=y;tr[na].tim=nc;tr[na].id=na;}//记得记录当前时间 else {c[++nc].x=now[x];c[nc].y=y;c[nc].ip=x;now[x]=y;} }//now的作用就体现出来了:我当前修改的时候队列是什么样的(有可能同一位置修改多次) sort(tr+1,tr+1+na,cmp); for (int i=1;i<=na;i++) { while (tim<tr[i].tim) {tim++;tchange(c[tim].ip,c[tim].y);}//坐时光机到未来 修改 while (tim>tr[i].tim) {tchange(c[tim].ip,c[tim].x);tim--;}//坐时光机到过去 改回来 //调区间 while (l<tr[i].l) {change(l,-1);l++;} while (l>tr[i].l) {l--;change(l,1);} while (r<tr[i].r) {r++;change(r,1);} while (r>tr[i].r) {change(r,-1);r--;} tr[i].ans=ans; } sort(tr+1,tr+1+na,cmp_id); for (int i=1;i<=na;i++) printf("%d\n",tr[i].ans); return 0; }
缺一个树上莫队……有空再来写把T T
相关文章推荐
- BZOJ 1878 [SDOI2009]HH的项链 (莫队算法)
- BZOJ 1878 [SDOI2009]HH的项链——离线+树状数组||莫队算法
- [bzoj1878][SDOI2009][HH的项链] (莫队算法)
- [BZOJ]1878: [SDOI2009]HH的项链 莫队算法
- 【BZOJ1878】【codevs2307】HH的项链,莫队算法
- BZOJ 1878 HH的项链(莫队算法)
- bzoj 1878: [SDOI2009]HH的项链(莫队算法)
- bzoj 1878: [SDOI2009]HH的项链(莫队算法)
- [SDOI2009][bzoj1878] HH的项链 [莫队模板题]
- [bzoj] 1878 HH的项链 || 莫队
- 【BZOJ-2453&2120】维护队列&数颜色 分块 + 带修莫队算法
- 【莫队 or 离线+树状数组】BZOJ1878(SDOI2009)[HH的项链]题解
- [bzoj1878][caioj1445][莫队]HH的项链
- [BZOJ2120][带修改莫队算法]数颜色
- bzoj1878 [SDOI2009]HH的项链【莫队】
- BZOJ1878(SDOI2009)[HH的项链]题解--莫队
- Bzoj 1878: [SDOI2009]HH的项链 莫队
- BZOJ 1878 [SDOI2009]HH的项链 莫队
- BZOJ 2120 数颜色&2453 维护队列 [带修改的莫队算法]【学习笔记】
- BZOJ 1878 SDOI2009 HH的项链 树状数组/莫队算法