Tag-莫队

Icontofig 's Blog // 我们的征程是星辰大海!Away From OI,Come to ICPC(查看友链请点About Me)

 2019-07-30 12:14:20 |  0 Comments  |  莫队 博弈论 NIM博弈

2019 Multi-University Training Contest 3 Game 经典NIM博弈SG函数 + 带修改莫队

题目大意

先手选出区间L-R的连续的石子堆,后手可以交换第pos 和 pos+1堆石子,然后再从L-R选出区间l-r的石子两人进行取石子博弈。

问在L-R中有多少种后手取l-r的方案能够使得先手必胜。

题解

这题是一个经典的NIM博弈。我们需要用SG函数来解决。

我们考虑前缀SG函数异或和,区间L-R内有多少选择l-r的方案使得先手必胜这一点我们可以转化为有都少方案使得先手必败(SG函数异或和为0)

l-r的SG函数异或和为0,即前缀异或和ssg满足ssg[r] = ssg[l-1],问题转化为统计区间中有多少ssg相等的点对数。

然后我们使用区间点对总数减去区间中ssg相等的点对数就是当前询问的答案。点对问题我们通常使用莫队的分治思想来处理。

然而此题还有交换石子堆的操作,我们能够发现,交换实际上只对一个前缀异或和有影响,所以交换操作实际上就是一个单点修改。

于是我们使用带修改莫队处理整个题目。

代码

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <algorithm>
#include <cmath>
using namespace std;
typedef long long ll;
int get_num(){
    int num = 0;
    char c;
    bool flag = false;
    while((c = getchar()) == ' ' || c == '\r' || c == '\n');
    if(c == '-')
        flag = true;
    else num = c - '0';
    while(isdigit(c = getchar()))
        num = (num<<3) + (num << 1) + c - '0';
    return (flag ? -1 : 1) * num;
}
const int maxn = 1e5+5;
int n,m;
struct query{
    int l,r,t,id;
}q[maxn];
int belong[maxn];
int upd[maxn];
int qcnt,dcnt;
int a[maxn],b[ma
 2019-05-19 21:03:32 |  0 Comments  |  莫队 树状数组

XTCPC2019 湘潭邀请赛 Problem C Chika and Friendly Pairs (莫队+树状数组)

 

题意:

给定n个数,m条询问,每个询问中包含l、r查询这个区间[l,r]中有多少个数对满足两个数的绝对值之差小于等于K

题解:

上来一看区间查询,第一反应一般都是线段树/树状数组等数据结构。

但是看一下这道题要求什么,求区间内满足条件的数对个数。

我好像没有见过能维护数对个数的树状数组或者线段树什么的。

对于数对而言,一般都应该先枚举其中一个数,剩下的才能去判断吧。

如果两个数i,j满足绝对值之差小于等于K,那么肯定能有这样一个关系式:ik<=j<=i+k

这样的话,对于某个区间内的所有数,能跟当前枚举出来的数x组成满足题意的数对的,一定在[x-k,x+k]这个区间里,我们只需要统计这个区间有多少个数就行了,这样每一侧的操作是O(Llogn)(L代表区间长度)的。

但是现在问的是对于某个给定的区间内的所有数能够组成的合法数对个数,我们就不能对区间每个数都像上面这样子暴力去统计了。那要怎么办呢?

答案就是使用莫队算法,里层查找答案时使用树状数组。

使用莫队算法能够使得我们有效利用重叠区间所统计的答案,而不必过多的修改统计树状数组来查询答案。

莫队的基本做法就是:先√n对区间分块,然后按照左端点所属块的序号为第一关键字,右端点大小为第二关键字对询问操作进行排序,然后设置l和r代表当前的位置指针, 枚举询问,不断移动l和r使之与当前的询问的l和r重合,期间添加修改信息。

对于此题,在l,r移动时,区间每加入一个数,先用树状数组统计当前区间内的满足条件的数的个数,然后将此数插入树状数组中。区间中要移除一个数的时候类似,不过两个步骤要反过来一下。读者可以自行理解一下为什么。

此题还有一个关键点是,离散化数x、x-k,x+k所组成的新数组,否则数太大树状数组会开不下的。然后离散化后的标号要O(n)用数组处理,不能使用map记录(map的查询复杂度是O(logn)的,会超时)。

总体复杂度O(n√nlogn)(实际上达不到这么大好像)

代码

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <cstdlib>
#include <algorithm>
#include <map>
using namespace std;
const int maxn = 1e5+5;
map<int,int>u
Title - Artist
0:00