7.区间乘法及区间加法,单点求和
给出一个长为 的数列,以及 个操作,操作涉及区间乘法,区间加法,单点询问。
数据范围:
线段树的经典模板题(雾 (*•̀ㅂ•́)و(不过我改成区间求和过去TLE了
思想还是维护块的乘积,加的值
- 单点查询时
- 对于区间修改涉及一个操作,即涉及不完整块的操作,首先重置对应整块的(因为加法乘法顺序的原因必须后再进行修改)
- 区间加:不完整的块后更新,整块更新
- 区间乘:不完整块后更新,整块更新,两者
#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cmath>
#include <vector>
using namespace std;
using LL = long long;
const int MOD = 1e4 + 7;
int N;
LL block, val[100005], blg[100005];
LL atag[1005], mtag[1005], sum[1005];
void add(int l, int r, int key);
void multi(int l, int r, int key);
int main(){
ios::sync_with_stdio(false);
int i;
cin >> N, block = sqrt(N);
for(i = 1; i <= N; i++) cin >> val[i];
for(i = 1; i <= N; i++)
blg[i] = (i - 1) / block + 1;
for(i = 1; i <= blg[N]; i++) mtag[i] = 1, atag[i] = 0;
for(i = 1; i <= N; i++){
int op, x, y, key;
cin >> op >> x >> y >> key;
if(op == 0) add(x, y, key);
else if(op == 1) multi(x, y, key);
else if(op == 2) cout << (val[y] * mtag[blg[y]] + atag[blg[y]]) % MOD << endl;
}
return 0;
}
void reset(int block_id);
void add(int l, int r, int key){
int i, top;
reset(blg[l]);
for(i = l, top = min(blg[l] * block, 1LL * r); i <= top; i++){
val[i] += key, val[i] %= MOD;
sum[blg[i]] += key, sum[blg[i]] %= MOD;
}
if(blg[l] != blg[r]){
reset(blg[r]);
for(i = (blg[r] - 1) * block + 1; i <= r; i++){
val[i] += key, val[i] %= MOD;
sum[blg[i]] += key, sum[blg[i]] %= MOD;
}
}
for(i = blg[l] + 1; i <= blg[r] - 1; i++){
sum[i] += block * key, sum[i] %= MOD;
atag[i] += key, atag[i] %= MOD;
}
}
void multi(int l, int r, int key){
int i, top;
reset(blg[l]);
for(i = l, top = min(blg[l] * block, 1LL * r); i <= top; i++){
sum[blg[i]] = (sum[blg[i]] - val[i] + MOD) % MOD;
val[i] *= key, val[i] %= MOD;
sum[blg[i]] += val[i], sum[blg[i]] %= MOD;
}
if(blg[l] != blg[r]){
reset(blg[r]);
for(i = (blg[r] - 1) * block + 1; i <= r; i++){
sum[blg[i]] = (sum[blg[i]] - val[i] + MOD) % MOD;
val[i] *= key, val[i] %= MOD;
sum[blg[i]] += val[i], sum[blg[i]] %= MOD;
}
}
for(i = blg[l] + 1; i <= blg[r] - 1; i++){
sum[i] *= key, sum[i] %= MOD;
atag[i] *= key, atag[i] %= MOD;
mtag[i] *= key, mtag[i] %= MOD;
}
}
void reset(int x){
int i, top;
for(i = (x - 1) * block + 1, top = min(x * block, 1LL * N); i <= top; i++)
val[i] = (val[i] * mtag[blg[i]] + atag[blg[i]]) % MOD;
mtag[x] = 1, atag[x] = 0;
}
8. 区间染色,区间颜色种类查询
给出一个长为 的数列,以及 个操作,操作涉及区间询问等于一个数 的元素,并将这个区间的所有元素改为 。
数据范围:
很诡异的操作,但仔细分析和第五题有点像.
可以发现虽然一开始一个块颜色很多,但经过多次染色后很多区间都是相同的颜色
操作即变为:
- 不完整块,先统计再标记对应块颜色不唯一
- 完整块,颜色唯一则直接判断,否则直接遍历(若数据十分不随机可以考虑记忆化)
若数据较为随机,每次操作摊还也是
#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cmath>
using namespace std;
using LL = long long;
LL N;
LL block, val[100005], blg[100005];
LL color[1005];
int update(int, int, int);
int main(){
ios::sync_with_stdio(false);
int i;
cin >> N, block = sqrt(N);
for(i = 1; i <= N; i++) cin >> val[i];
for(i = 1; i <= N; i++) blg[i] = (i - 1) / block + 1;
for(i = 1; i <= blg[N]; i++) color[i] = -1e11;
for(i = 1; i <= N; i++){
int x, y, key;
cin >> x >> y >> key;
cout << update(x, y, key) << endl;
}
return 0;
}
void reset(int);
int update(int l, int r, int key){
int i, j, top, res = 0;
top = min(blg[l] * block, 1LL * r);
if(color[blg[l]] != -1e11)
res += (top - l + 1) * (color[blg[l]] == key);
else{
for(i = l; i <= top; i++)
if(val[i] == key) res++;
}
reset(blg[l]);
color[blg[l]] = -1e11;
for(i = l; i <= top; i++) val[i] = key;
if(blg[l] != blg[r]){
if(color[blg[r]] != -1e11)
res += (r - (blg[r] - 1) * block) * (color[blg[r]] == key);
else{
for(i = (blg[r] - 1) * block + 1; i <= r; i++)
if(val[i] == key) res++;
}
reset(blg[r]);
color[blg[r]] = -1e11;
for(i = (blg[r] - 1) * block + 1; i <= r; i++) val[i] = key;
}
for(i = blg[l] + 1; i <= blg[r] - 1; i++){
if(color[i] == -1e11){
for(j = (i - 1) * block + 1; j <= block * i; j++)
if(val[j] == key) res++;
}
else
res += block * (color[i] == key);
color[i] = key;
}
return res;
}
void reset(int x){
if(color[x] == -1e11) return;
int i, top;
for(i = (x - 1) * block + 1, top = min(x * block, 1LL * N); i <= top; i++)
val[i] = color[blg[i]];
}
9.区间无修改众数查询(在线算法)
给出一个长为的数列,以及 个操作,操作涉及询问区间的最小众数。
数据范围:
首先将每个数离散化,再开辟一个记录出现的位置(这样就可以利用操作得到一个数在某区间的出现次数)
- 对于整块,首先预处理可以得到: 个整块的众数(这里是)暴力枚举
- 查询时,众数在不完整的块中或者为整块整体的众数(直接得到).再进行枚举判断即可
可以得到分块大小取较优
#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cmath>
#include <vector>
#include <set>
#include <map>
using namespace std;
int N;
int block, val[100005], blg[100005];
int mode[6005][6005];
int hashx[100005], cnt[100005];
map<int, int> mp;
vector<int> ve[100005];
void pre_mode(int block_id);
int query(int, int);
int main(){
ios::sync_with_stdio(false);
int i, ind;
cin >> N, block = sqrt(N) / log(N);
for(i = 1, ind = 0; i <= N; i++){
cin >> val[i];
if(!mp[val[i]]){
mp[val[i]] = ++ind;
hashx[ind] = val[i];
}
val[i] = mp[val[i]];
ve[val[i]].push_back(i);
}
for(i = 1; i <= N; i++) blg[i] = (i - 1) / block + 1;
for(i = 1; i <= blg[N]; i++) pre_mode(i);
for(i = 1; i <= N; i++){
int x, y;
cin >> x >> y;
cout << hashx[query(x, y)] << endl;
}
return 0;
}
int gettimes(int, int, int);
int query(int l, int r){
int res, mx;
res = mode[blg[l] + 1][blg[r] - 1];
mx = gettimes(l, r, res);
int i, top;
for(i = l, top = min(blg[l] * block, r); i <= top; i++){
int tmp = gettimes(l, r, val[i]);
if(tmp > mx || (tmp == mx && hashx[val[i]] < hashx[res]))
res = val[i], mx = tmp;
}
if(blg[l] != blg[r])
for(i = (blg[r] - 1) * block + 1; i <= r; i++){
int tmp = gettimes(l, r, val[i]);
if(tmp > mx || (tmp == mx && hashx[val[i]] < hashx[res]))
res = val[i], mx = tmp;
}
return res;
}
int gettimes(int l, int r, int x){
return upper_bound(ve[x].begin(), ve[x].end(), r) - lower_bound(ve[x].begin(), ve[x].end(), l);
}
void pre_mode(int x){
memset(cnt, 0, sizeof(cnt));
int i, j, mx = 0, res = 0;
for(i = (x - 1) * block + 1; i <= N; i++){
cnt[val[i]]++;
j = blg[i];
if(cnt[val[i]] > mx || (cnt[val[i]] == mx && hashx[val[i]] < hashx[res]))
mx = cnt[val[i]], res = val[i];
mode[x][j] = res;
}
}