参考hzw大佬的blog
1
给出一个长为n的数列,以及n个操作,操作涉及区间加法,单点查值。
嗯 分块板子啦
贴下主要部分的代码
struct Block{
int size, cnt, pos[N];
int atag[N];
inline int query(int x){
return a[x] + atag[pos[x]];
}
inline void add(int x, int y, int d){
for(int i = x; i <= min(pos[x] * size, y); ++i)
a[i] += d;
if(pos[x] != pos[y])
for(int i = (pos[y] - 1) * size + 1; i <= y; ++i)
a[i] += d;
for(int i = pos[x] + 1; i <= pos[y] - 1; ++i)
atag[i] += d;
}
}bl;
2
给出一个长为n的数列,以及n个操作,操作涉及区间加法,询问区间内小于某个值x的元素个数
考察块内排序的维护
struct Block{
int size, cnt, pos[N];
int atag[sqN];
vector<int> ve[sqN];
void reset(int x){
ve[x].clear();
for(int i = (x - 1) * size + 1; i <= min(x * size, n); ++i){//min(, n)!!!
ve[x].push_back(a[i]);
}
sort(ve[x].begin(), ve[x].end());
}
inline int query(int x, int y, int d){
int res = 0;
for(int i = x; i <= min(pos[x] * size, y); ++i){
if(a[i] < d - atag[pos[x]]) ++res;
}
if(pos[x] != pos[y])
for(int i = (pos[y] - 1) * size + 1; i <= y; ++i)
if(a[i] < d - atag[pos[y]]) ++res;
for(int i = pos[x] + 1; i <= pos[y] - 1; ++i){
res += lower_bound(ve[i].begin(), ve[i].end(), d - atag[i]) - ve[i].begin();
}
return res;
}
inline void add(int x, int y, int d){
for(int i = x; i <= min(pos[x] * size, y); ++i)
a[i] += d;
reset(pos[x]);
if(pos[x] != pos[y]){
for(int i = (pos[y] - 1) * size + 1; i <= y; ++i)
a[i] += d;
reset(pos[y]);
}
for(int i = pos[x] + 1; i <= pos[y] - 1; ++i)
atag[i] += d;
}
}bl;
3
给出一个长为n的数列,以及n个操作,操作涉及区间加法,询问区间内小于某个值x的前驱(比其小的最大元素)。
考察维护块内前驱(当然后继同理
struct Block{
int size, cnt, pos[N];
int atag[sqN];
set<int> st[sqN];
inline int pre(int x, int y, int d){
int res = -inf;
set<int> :: iterator it;
for(int i = x; i <= min(pos[x] * size, y); ++i){
if(a[i] + atag[pos[x]] < d) res = max(res, a[i] + atag[pos[x]]);
}
if(pos[x] != pos[y])
for(int i = (pos[y] - 1) * size + 1; i <= y; ++i)
if(a[i] + atag[pos[y]] < d) res = max(res, a[i] + atag[pos[y]]);
for(int i = pos[x] + 1; i <= pos[y] - 1; ++i){
it = st[i].lower_bound(d - atag[i]);
if(it == st[i].begin()) continue;
--it;
res = max(*it + atag[i], res);
}
return res == -inf ? -1 : res;
}
inline void add(int x, int y, int d){
for(int i = x; i <= min(pos[x] * size, y); ++i){
st[pos[x]].erase(a[i]);
a[i] += d;
st[pos[x]].insert(a[i]);
}
if(pos[x] != pos[y]){
for(int i = (pos[y] - 1) * size + 1; i <= y; ++i){
st[pos[y]].erase(a[i]);
a[i] += d;
st[pos[y]].insert(a[i]);
}
}
for(int i = pos[x] + 1; i <= pos[y] - 1; ++i)
atag[i] += d;
}
}bl;
4
给出一个长为n的数列,以及n个操作,操作涉及区间加法,区间求和。
记得开longlong哦
struct Block{
int size, cnt, pos[N];
long long atag[sqN], sum[sqN];
inline long long query(int x, int y, long long d){
long long res = 0;
for(int i = x; i <= min(pos[x] * size, y); ++i)
res += a[i] + atag[pos[x]];
if(pos[x] != pos[y])
for(int i = (pos[y] - 1) * size + 1; i <= y; ++i)
res += a[i] + atag[pos[y]];
res %= d;
for(int i = pos[x] + 1; i <= pos[y] - 1; ++i){
res += sum[i] + atag[i] * size;
res %= d;
}
return res % d;
}
inline void add(int x, int y, long long d){
for(int i = x; i <= min(pos[x] * size, y); ++i){
sum[pos[x]] += d;
a[i] += d;
}
if(pos[x] != pos[y]){
for(int i = (pos[y] - 1) * size + 1; i <= y; ++i){
sum[pos[y]] += d;
a[i] += d;
}
}
for(int i = pos[x] + 1; i <= pos[y] - 1; ++i)
atag[i] += d;
}
}bl;
5
给出一个长为n的数列,以及n个操作,操作涉及区间开方,区间求和。
一个数经过几次开方就会变成0或1
所以维护一下这个块有没有全变成0或1就好啦
struct Block{
int size, cnt, pos[N];
long long sum[sqN];
bool flag[sqN];
inline long long query(int x, int y){
long long res = 0;
for(int i = x; i <= min(pos[x] * size, y); ++i)
res += a[i];
if(pos[x] != pos[y])
for(int i = (pos[y] - 1) * size + 1; i <= y; ++i)
res += a[i];
for(int i = pos[x] + 1; i <= pos[y] - 1; ++i){
res += sum[i];
}
return res;
}
inline bool sq(int x){
bool ret = 1;
for(int i = (x - 1) * size + 1; i <= min(n, x * size); ++i){
a[i] = sqrt(a[i]);
if(ret && a[i] != 0 && a[i] != 1) ret = 0;
}
sum[x] = 0;
for(int i = (x - 1) * size + 1; i <= min(n, x * size); ++i){
sum[x] += a[i];
}
return ret;
}
inline void modify(int x, int y){
for(int i = x; i <= min(pos[x] * size, y); ++i){
sum[pos[x]] -= a[i];
a[i] = sqrt(a[i]);
sum[pos[x]] += a[i];
}
if(pos[x] != pos[y]){
for(int i = (pos[y] - 1) * size + 1; i <= y; ++i){
sum[pos[y]] -= a[i];
a[i] = sqrt(a[i]);
sum[pos[y]] += a[i];
}
}
for(int i = pos[x] + 1; i <= pos[y] - 1; ++i)
if(!flag[i]) flag[i] = sq(i);
}
}bl;
6
给出一个长为n的数列,以及n个操作,操作涉及单点插入,单点询问,数据随机生成。
本题随机生成数据 用链表什么的直接插就行
不随机就要重构 有三种做法
1.每根号n次插入后,重新把数列平均分一下块
2.当某个块过大时重构
3.把特别大的块分成两半
struct Block{
int size, cnt, pos[N];
int stk[N], top;
vector<int> ve[sqN];
pair<int, int> query(int x){
int i;
for(i = 1; x > ve[i].size(); ++i)
x -= ve[i].size();
return make_pair(i, x - 1);
}
void rebuild(){
top = 0;
for(int i = 1; i <= cnt; ++i){
for(vector<int> :: iterator j = ve[i].begin(); j != ve[i].end(); ++j){
stk[++top] = *j;
}
ve[i].clear();
}
size = sqrt(top);
for(int i = 1; i <= top; ++i){
pos[i] = (i - 1) / size + 1;//是/不是%!!!!!
ve[pos[i]].push_back(stk[i]);
}
cnt = pos[top];
}
void ins(int x, int y){
pair<int, int> t = query(x);
ve[t.first].insert(ve[t.first].begin() + t.second, y);
}
}bl;
7
给出一个长为 n 的数列,以及 n 个操作,操作涉及区间乘法,区间加法,单点询问。
这道题就是取一个优先级
和洛谷线段树2差不多
longlong竟然比int慢这么多【雾
struct Block{
int size, cnt, pos[N];
int atag[N], mtag[N];
int query(int x){
return (a[x] * mtag[pos[x]] + atag[pos[x]]) % P;
}
inline void init(){
for(int i = 1; i <= cnt; ++i){
mtag[i] = 1;
}
}
inline void reset(int x){
for(int i = (x - 1) * size + 1; i <= min(x * size, n); ++i){
a[i] = (a[i] * mtag[x] + atag[x]) % P;
}
mtag[x] = 1, atag[x] = 0;
}
void add(int x, int y, int d){
reset(pos[x]);
for(int i = x; i <= min(y, pos[x] * size); ++i){
a[i] = (a[i] + d) % P;
}
if(pos[x] != pos[y]){
reset(pos[y]);
for(int i = (pos[y] - 1) * size + 1; i <= y; ++i){
a[i] = (a[i] + d) % P;
}
}
for(int i = pos[x] + 1; i <= pos[y] - 1; ++i){
atag[i] = (atag[i] + d) % P;
}
}
void mul(int x, int y, int d){
reset(pos[x]);
for(int i = x; i <= min(y, pos[x] * size); ++i){
a[i] = (a[i] * d) % P;
}
if(pos[x] != pos[y]){
reset(pos[y]);
for(int i = (pos[y] - 1) * size + 1; i <= y; ++i){
a[i] = (a[i] * d) % P;
}
}
for(int i = pos[x] + 1; i <= pos[y] - 1; ++i){
atag[i] = (atag[i] * d) % P;
mtag[i] = (mtag[i] * d) % P;
}//注意这里的处理哦
}
}bl;
8
给出一个长为 n 的数列,以及 n 个操作,操作涉及区间询问等于一个数 c 的元素,并将这个区间的所有元素改为 c。
哇这一波复杂度证明真心阔以
今天才理解lxl那节课上讲的染色qvq
和开方那道题有一点类似
都是只能一个一个修改的东西
中间的值一样了就标记一下
这样虽然看起来不太可行
但实际上你修改两个端点 最多只能拆两个同色的块
要修改上 √n 个才能把复杂度拉到
O
(
n
)
O(n)
O(n)
(有√n个不同色的块 每个需要√n复杂度维护)
那这么一平摊 又
O
(
n
√
n
)
O(n √n)
O(n√n)了。。。
懒得打根号
struct Block{
int size, cnt, pos[N];
int col[sqN];
void init(){
memset(col, -1, sizeof(col));
}
void reset(int x){
if(col[x] == -1) return ;
for(int i = (x - 1) * size + 1; i <= min(size * x, n); ++i)
a[i] = col[x];
col[x] = -1;
}
int modify(int x, int y, int d){
int res = 0;
reset(pos[x]);
for(int i = x; i <= min(y, pos[x] * size); ++i){
if(a[i] == d) ++res;
else a[i] = d;
}
if(pos[x] != pos[y]){
reset(pos[y]);
for(int i = (pos[y] - 1) * size + 1; i <= y; ++i){
if(a[i] == d) ++res;
else a[i] = d;
}
}
for(int i = pos[x] + 1; i <= pos[y] - 1; ++i){
if(~col[i]){
if(col[i] != d) col[i] = d;
else res += size;
}
else {
for(int j = (i - 1) * size + 1; j <= i * size; ++j){
if(a[j] == d) ++res;
else a[j] = d;
}
col[i] = d;
}
}
return res;
}
}bl;
9
给出一个长为n的数列,以及n个操作,操作涉及询问区间的最小众数。
忍住没有用莫队写
这是利用了本质的√n大法吧。。。
struct Block{
int size, cnt, pos[N];
int colcnt[N];
void init(int x){
int mx = 0, res;
memset(colcnt, 0, sizeof(colcnt));
for(int i = (x - 1) * size + 1, y; i <= n; ++i){
y = ls[a[i]]; ++colcnt[y];
if(mx < colcnt[y] || (mx == colcnt[y] && val[res] > val[y])){
mx = colcnt[y]; res = y;
}
g[x][pos[i]] = mx;
f[x][pos[i]] = res;
}
}
int ve_q(int x, int y, int c){
return upper_bound(ve[c].begin(), ve[c].end(), y) - lower_bound(ve[c].begin(), ve[c].end(), x);
}
int query(int x, int y){
int res = 0, mx = 0;
if(pos[x] < pos[y] - 1){
res = f[pos[x] + 1][pos[y] - 1]; mx = g[pos[x] + 1][pos[y] - 1];
}
for(int i = x, t, col; i <= min(y, pos[x] * size); ++i){
col = ls[a[i]];
t = ve_q(x, y, col);
if(t > mx || (t == mx && val[col] < val[res])){
mx = t, res = col;
}
}
if(pos[y] != pos[x])
for(int i = (pos[y] - 1) * size + 1, t, col; i <= y; ++i){
col = ls[a[i]];
t = ve_q(x, y, col);
if(t > mx || (t == mx && val[col] < val[res])){
mx = t, res = col;
}
}
return res;
}
}bl;