初识莫队——小Z的袜子
以前一直觉得莫队是多么高大上的一种算法,然而仔细看了下发现其实并不复杂,实质上就是技巧性的暴力美学。
在我看来莫队是一种分块排序后降低复杂度的算法,当答案可以通过左右端点一个一个移动维护出来的时候就可以使用莫队了。
比如给你4个区间
(1, 2)
(99, 100)
(3, 4)
(101, 102)
如果你是傻傻的按照给定的顺序去移动左右端点,那就是先计算出(1,2)区间的值,然后左端点先从1移动到99,右端点从2移动到100,计算完(99,100)区间内的值,又哼哧哼哧的左端点从99移回3,右端点从100移回4,计算完(3,4)后又移动到(101,102),显然这样实在是太蠢了。
正常人都应该想到,那我把计算顺序改成(1,2)(3,4)(99,100)(101,102)不就行了,但是因为区间是二维的,简单的排序似乎是行不通的(通过lhs或者rhs来排序似乎都不太好)
莫队为我们提供了一个排序方法,假设有Q个询问,区间范围是1~N,那么可以对左端点分块,分成根号N块,这样就先把不同块的左端点排好序了,接下来同一块的询问根据右端点排序,这样就形成了一个(比较科学)的序列,可以使左右端点移动的距离变小,变相减小了复杂度。
实际上莫队就是一个科学的二维数组排序方法嘛
然后对于这道题因为是求概率,每次如果都维护一个分数很困难,所以选择每次维护分子和分母,这样这个概率公式就很好求了,分母只与区间长度有关,分子就是(每种袜子数目*(每种袜子数目-1))然后求和,化简后发现只要求(每种袜子数目)的平方的和就可以了,最后求一下gcd把分数表示成最简形式
#include <iostream>
#include <string.h>
#include <cstdio>
#include <vector>
#include <map>
#include <math.h>
#include <string>
#include <algorithm>
#include <time.h> #define SIGMA_SIZE 26
#define lson rt<<1
#define rson rt<<1|1
#define lowbit(x) (x&-x)
#define goe(i, a, b) for(int i=a; i<=b; i++)
#define go(i, a, b) for(int i = a; i < b; i++);
#pragma warning ( disable : 4996 ) using namespace std;
typedef long long LL;
inline LL LMax(LL a,LL b) { return a>b?a:b; }
inline LL LMin(LL a,LL b) { return a>b?b:a; }
inline LL lgcd( LL a, LL b ) { return b==?a:lgcd(b,a%b); }
inline LL llcm( LL a, LL b ) { return a/lgcd(a,b)*b; } //a*b = gcd*lcm
inline int Max(int a,int b) { return a>b?a:b; }
inline int Min(int a,int b) { return a>b?b:a; }
inline int gcd( int a, int b ) { return b==?a:gcd(b,a%b); }
inline int lcm( int a, int b ) { return a/gcd(a,b)*b; } //a*b = gcd*lcm
const LL INF = 0x3f3f3f3f3f3f3f3f;
const LL mod = ;
const double eps = 1e-;
const int inf = 0x3f3f3f3f;
const int maxk = 5e4+;
const int maxn = 3e4+; int belong[maxk], wazi[maxk];
int N, M, unit;
LL ans, cnt[maxk], fina[maxk], finb[maxk];
struct query {
int lhs, rhs, id;
LL a, b; //分子和分母
}q[maxk]; bool cmp(const query& a, const query& b)
{
if (belong[a.lhs] == belong[b.lhs]) return a.rhs < b.rhs;
return a.lhs < b.lhs;
} void revise( int x, int d )
{
//ans先减去以前的贡献再加上现在的贡献
ans -= cnt[wazi[x]]*cnt[wazi[x]];
cnt[wazi[x]] += d;
ans += cnt[wazi[x]]*cnt[wazi[x]];
} void read()
{
scanf("%d %d", &N, &M); unit = sqrt(N); goe(i, , N) { scanf("%d", &wazi[i]); belong[i] = i/unit+; }
goe(i, , M) { scanf("%d %d", &q[i].lhs, &q[i].rhs); q[i].id = i; }
sort(q+, q++M, cmp); } int main()
{
read(); int l = , r = ;
ans = ; goe(i, , M)
{
//如果是减操作,则先要执行revise,
//注意顺序
while(l < q[i].lhs) { revise(l, -); l++; }
while(l > q[i].lhs) { l--; revise(l, ); }
while(r < q[i].rhs) { r++; revise(r, ); }
while(r > q[i].rhs) { revise(r, -); r--; } if ( q[i].lhs == q[i].rhs ) { q[i].a = ; q[i].b = ; continue; }
q[i].a = ans - (q[i].rhs - q[i].lhs + );
q[i].b = (LL)*(q[i].rhs - q[i].lhs)*(q[i].rhs - q[i].lhs + ); LL Gcd = lgcd(q[i].a, q[i].b);
q[i].a /= Gcd;
q[i].b /= Gcd;
} goe(i, , M)
{
fina[q[i].id] = q[i].a;
finb[q[i].id] = q[i].b;
} goe(i, , M)
printf("%lld/%lld\n", fina[i], finb[i]); return ;
}
待修改的莫队,这就有点难受了,原先的二维区间变成了三维,加了一个时间维度(怎么感觉怪怪的),大意就是原先每个结构体多存储一个了时间变量,修改操作也通过时间变量储存,并且通过change这一数据结构将一系列修改的数据存储下来(即能通过时间确定修改的值,就像能操纵时间,随时可以通过时间还原原来的序列或者变化成修改后的序列),然后就是在原始莫队的基础上加上一个时间维度的移动,不得不说这个算法真是朴素而美妙...
HYSBZ 2120 数颜色---模板题
#include <iostream>
#include <string.h>
#include <cstdio>
#include <vector>
#include <map>
#include <math.h>
#include <string>
#include <algorithm>
#include <time.h> #define SIGMA_SIZE 26
#define lson rt<<1
#define rson rt<<1|1
#define lowbit(x) (x&-x)
#define foe(i, a, b) for(int i=a; i<=b; i++)
#define fo(i, a, b) for(int i = a; i < b; i++);
#pragma warning ( disable : 4996 ) using namespace std;
typedef long long LL;
inline LL LMax(LL a,LL b) { return a>b?a:b; }
inline LL LMin(LL a,LL b) { return a>b?b:a; }
inline LL lgcd( LL a, LL b ) { return b==?a:lgcd(b,a%b); }
inline LL llcm( LL a, LL b ) { return a/lgcd(a,b)*b; } //a*b = gcd*lcm
inline int Max(int a,int b) { return a>b?a:b; }
inline int Min(int a,int b) { return a>b?b:a; }
inline int gcd( int a, int b ) { return b==?a:gcd(b,a%b); }
inline int lcm( int a, int b ) { return a/gcd(a,b)*b; } //a*b = gcd*lcm
const LL INF = 0x3f3f3f3f3f3f3f3f;
const LL mod = ;
const double eps = 1e-;
const int inf = 0x3f3f3f3f;
const int maxk = 1e6+;
const int maxn = 1e4+; int belong[maxn], num[maxn], now[maxn], fin[maxn];
int N, Q, unit, l, r;
int t, T, Time, ans;
int cnt[maxk]; struct query {
int lhs, rhs, tim, id;
query() {}
query(int l, int r, int t, int i)
: lhs(l), rhs(r), tim(t), id(i) {} }q[maxn]; // pos表示修改的位置,new表示修改后的值,Old表示修改前的值
struct change {
int pos, New, Old;
change() {}
change(int p, int n, int o)
: pos(p), New(n), Old(o) {}
}c[maxn]; bool cmp(const query& a, const query& b)
{
if (belong[a.lhs != b.lhs]) return a.lhs < b.lhs;
if (belong[a.rhs != b.rhs]) return a.rhs < b.rhs;
return a.tim < b.tim;
} void read()
{
//最优分块策略不再是根号
scanf("%d %d", &N, &Q); unit = pow((double)N, (double)0.666666);
foe(i, , N) { scanf("%d", &num[i]); now[i] = num[i]; belong[i] = i/unit+; } char str[];
int x, y; t = T = ;
foe(i, , Q)
{
scanf("%s %d %d", str, &x, &y);
if (str[] == 'Q') q[++t] = query(x, y, T, t);
if (str[] == 'R') { c[++T] = change(x, y, now[x]); now[x] = y; }
}
sort(q+, q+t+, cmp);
} void revise(int x, int d)
{
cnt[x] += d;
if (d > )
ans += (cnt[x] == ); //只有从0加到1才会增加一种颜色数量
if (d < )
ans -= (cnt[x] == );
} void reviseT(int x, int d)
{
if ( x >= l && x <= r )
{
revise(d, );
revise(num[x], -);
}
num[x] = d;
} int main()
{
read(); l = ; r = ; Time = ;
foe(i, , t)
{
//若修改时间小于tim,则要添加新的修改
while(Time < q[i].tim) { Time++; reviseT(c[Time].pos, c[Time].New); }
//若time大于tim,则还原老修改
while(Time > q[i].tim) { reviseT(c[Time].pos, c[Time].Old); Time--; } while(l < q[i].lhs) { revise(num[l], -); l++; }
while(l > q[i].lhs) { l--; revise(num[l], ); }
while(r < q[i].rhs) { r++; revise(num[r], ); }
while(r > q[i].rhs) { revise(num[r], -); r--; } fin[q[i].id] = ans; } foe(i, , t)
printf("%d\n", fin[i]); return ;
}
初识莫队——小Z的袜子的更多相关文章
- 莫队-小Z的袜子
----普通莫队 首先清楚概率怎么求假设我们要求从区间l到r中拿出一对袜子的概率sum[i]为第i种袜子在l到r中的数量 $$\frac{\sum_{i=l}^{r} {[sum[i] \times ...
- BZOJ 2038: [2009国家集训队]小Z的袜子(hose) [莫队算法]【学习笔记】
2038: [2009国家集训队]小Z的袜子(hose) Time Limit: 20 Sec Memory Limit: 259 MBSubmit: 7687 Solved: 3516[Subm ...
- 莫队算法 2038: [2009国家集训队]小Z的袜子(hose)
链接:http://www.lydsy.com/JudgeOnline/problem.php?id=2038 2038: [2009国家集训队]小Z的袜子(hose) Time Limit: 20 ...
- BZOJ-2038 小Z的袜子(hose) 莫队算法
2038: [2009国家集训队]小Z的袜子(hose) Time Limit: 20 Sec Memory Limit: 259 MB Submit: 5573 Solved: 2568 [Subm ...
- BZOJ 2038 [2009国家集训队]小Z的袜子 莫队
2038: [2009国家集训队]小Z的袜子(hose) 题目连接: http://www.lydsy.com/JudgeOnline/problem.php?id=2038 Descriptionw ...
- Bzoj 2038: [2009国家集训队]小Z的袜子(hose) 莫队,分块,暴力
2038: [2009国家集训队]小Z的袜子(hose) Time Limit: 20 Sec Memory Limit: 259 MBSubmit: 5763 Solved: 2660[Subm ...
- BZOJ2038: [2009国家集训队]小Z的袜子(hose) -- 莫队算法 ,,分块
2038: [2009国家集训队]小Z的袜子(hose) Time Limit: 20 Sec Memory Limit: 259 MBSubmit: 3577 Solved: 1652[Subm ...
- BZOJ 2038: [2009国家集训队]小Z的袜子(hose) ( 莫队 )
莫队..先按sqrt(n)分块, 然后按块的顺序对询问排序, 同块就按右端点排序. 然后就按排序后的顺序暴力求解即可. 时间复杂度O(n1.5) --------------------------- ...
- BZOJ 2038: [2009国家集训队]小Z的袜子(hose)【莫队算法裸题&&学习笔记】
2038: [2009国家集训队]小Z的袜子(hose) Time Limit: 20 Sec Memory Limit: 259 MBSubmit: 9894 Solved: 4561[Subm ...
随机推荐
- bzoj1568 Blue Mary
题意:P:加入一条一次函数.Q:询问x位置的最大函数值. 标程: #include<bits/stdc++.h> using namespace std; ; int q,x,n; dou ...
- 安装Docker 服务
curl -fsSL https://get.docker.com/ | sh 执行到这一部分出错: The program 'curl' is currently not installed. Yo ...
- el-table单元格新增、编辑、删除功能
<template> <div class="box"> <el-button class="addBtn" type=" ...
- JSF(JavaServer Faces)简介
JavaServer Faces (JSF) 是一种用于构建Java Web 应用程序的标准框架(是Java Community Process 规定的JSR-127标准).它提供了一种以组件为中心的 ...
- Pycharm新建文档的模板设置
下图演示的是关于python的文档的模板设置! 这样,以后的每一个新建的python的py文件,开头都会有下图中的两句:解释器路径与编码方式 步聚5的第二行内容打错了,应该是utf: #!/usr/b ...
- 14. TIM of STM32F103ZE
block diagram 14.3.1 Time-base unit 有三个基础的寄存器: 计数寄存器(TIMx_CNT) 预分配寄存器(TIMx_PSC), 自动重载寄存器(TIMx_ARR) 重 ...
- 《DSP using MATLAB》Problem 8.34
今天下了小雨,空气中泛起潮湿的味道,阴冷的感觉袭来,心情受到小小影响. 代码: hp2lpfre子函数 function [wpLP, wsLP, alpha] = hp2lpfre(wphp, ws ...
- Kindle电子书制作
text.html: <!DOCTYPE html> <html lang="en"> <head> <meta http-equiv=& ...
- 确认(confirm 消息对话框)语法:confirm(str); 消息对话框通常用于允许用户做选择的动作,如:“你对吗?”等。弹出对话框(包括一个确定按钮和一个取消按钮)
确认(confirm 消息对话框) confirm 消息对话框通常用于允许用户做选择的动作,如:"你对吗?"等.弹出对话框(包括一个确定按钮和一个取消按钮). 语法: confir ...
- LoadRunner如何调用外部函数
LoadRunner如何调用外部函数 使用 VuGen 时,可以调用在外部 DLL 中定义的函数.通过从脚本调用外部函数,可以降低脚本的内存使用量以及总体运行时间.要调用外部函数,需要加载定义了该函数 ...