分块基本 + 多维后缀和
1.区间众数
首先,预处理二维数组 f[i][j]表示,前 i 块中 j 的出现次数, 那么对于整块来说,f[r][j] - f[l - 1][j] 就表示 [l, r]块中,j 的出现次数
其次,预处理二维数组 g[i][j]表示,在第 i 块 到 第 j 块 中,出现最多的数的次数
这样,在查询时,只需要遍历一遍零散的块就够了,然后取 max
代码
#include<bits/stdc++.h>
const int K = 9;
const int MAXN = 6e4 + 10;
using namespace std;
int T;
int n,m;
int ans,shu;
int last;
int size,zs;
int cnt[MAXN];
struct node
{
int val;
int ord;
};
node k[MAXN];
int fi[MAXN];
int f[300][300];
int g[300][MAXN];
int st[300],en[300];
bool cmp(node a,node b)
{
return a.val < b.val;
}
void up(int &a,int b)
{
a = a > b ? a : b;
}
void init()
{
// init f
for(int i=0;i<=zs;i++)
for(int j=0;j<=zs;j++)
f[i][j] = 0;
for(int i=0;i<=zs;i++)
{
for(int j=1;j<=shu;j++) cnt[j] = 0;
int cntt = 0;
for(int j=i;j<=zs;j++)
{
for(int l=st[j];l<=en[j];l++)
up(cntt,++cnt[fi[l]]);
f[i][j] = cntt;
}
}
//init g
for(int i=0;i<=zs;i++)
for(int j=1;j<=shu;j++)
g[i][j] = 0;
for(int i=1;i<=shu;i++) cnt[i] = 0;
for(int i=0;i<=zs;i++)
{
for(int j=st[i];j<=en[i];j++) cnt[fi[j]]++;
for(int j=1;j<=shu;j++) g[i][j] = cnt[j];
}
}
int ask(int l,int r)
{
int L = (l >> K) ;
int R = (r >> K) ;
int res = 0;
if(L + 1 >= R)
{
for(int i=1;i<=shu;i++) cnt[i] = 0;
for(int i=l;i<=r;i++)
{
up(res,++cnt[fi[i]]);
}
return res;
}
res = f[L+1][R-1];
for(int i=1;i<=shu;i++) cnt[i] = 0;
for(int i=en[L];i>=l;i--)
{
up(res,(++cnt[fi[i]]) + g[R-1][fi[i]] - g[L][fi[i]]);
}
for(int i=st[R];i<=r;i++)
{
up(res,(++cnt[fi[i]]) + g[R-1][fi[i]] - g[L][fi[i]]);
}
return res;
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
cin>>T;
while(T--)
{
cin>>n>>m;
ans = 0;
for(int i=1;i<=n;i++) cin>>k[i].val,k[i].ord = i;
sort(k+1,k+n+1,cmp);
shu = 0;
for(int i=1;i<=n;i++)
{
if(k[i].val > k[i-1].val) shu++;
fi[k[i].ord] = shu;
}
zs = n >> K;
for(int i=1;i<=n;i++) en[(i>>K)] = i;
for(int i=n;i>=1;i--) st[(i>>K)] = i;
init();
int l,r;
for(int i=1;i<=m;i++)
{
cin>>l>>r;
ans = ask(l^ans,r^ans);
cout<<ans<<'\n';
}
// cout<<size<<" "<<p<<"\n";
// for(int i=0;i<=zs;i++) cout<<st[i]<<" ";
// cout<<'\n';
// for(int i=0;i<=zs;i++) cout<<en[i]<<" ";
}
return 0;
}
2.区间第k小
涉及两个操作: 对某区间都加一个数, 查询某区间内第k小的数
对第一个操作:定义一个数组用来记录某一块整体都加了多少,涉及到零散的,就单独遍历,加上这个数后再重新排序
对第二个操作:可以二分,假设这个第k小的数是mid,在每个块(预先排好序的)里二分,看是不是第k小的。
查看代码
#include<bits/stdc++.h>
const int K = 9;
const int MAXN = 5e4 + 10;
using namespace std;
int T;
int n,m;
struct node
{
int val;
int ord;
};
int st[310],en[310];
node k[MAXN];
node kk[MAXN];
int seg[310];
int op,ll,rr,s;
bool cmp(node a,node b){return a.val < b.val;}
void add(int l,int r,int x)
{
int L = l >> K;
int R = r >> K;
for(int i = L + 1;i <= R - 1;i++) seg[i] += x;
node a[1010],b[1010];
int cnt1 = 0,cnt2 = 0;
for(int i = st[L];i <= en[L];i++)
{
if(kk[i].ord >= l && kk[i].ord <= r)
{
kk[i].val += x;
k[kk[i].ord] = kk[i];
a[++cnt1] = kk[i];
}
else
{
b[++cnt2] = kk[i];
}
}
int poi = st[L];
int i = 1,j = 1;
while(i <= cnt1 && j <= cnt2)
{
node aa = a[i];
node bb = b[j];
if(aa.val > bb.val)
{
kk[poi] = bb;
j++;
}
else
{
kk[poi] = aa;
i++;
}
poi++;
}
while(i <= cnt1) kk[poi++] = a[i++];
while(j <= cnt2) kk[poi++] = b[j++];
if(L == R) return ;
i = j = 1;
cnt1 = cnt2 = 0;
for(int i = st[R];i <= en[R];i++)
{
if(kk[i].ord >= l && kk[i].ord <= r)
{
kk[i].val += x;
k[kk[i].ord] = kk[i];
a[++cnt1] = kk[i];
}
else
{
b[++cnt2] = kk[i];
}
}
poi = st[R];
while(i <= cnt1 && j <= cnt2)
{
node aa = a[i];
node bb = b[j];
if(aa.val > bb.val)
{
kk[poi++] = bb;
j++;
}
else
{
kk[poi++] = aa;
i++;
}
}
while(i <= cnt1) kk[poi++] = a[i++];
while(j <= cnt2) kk[poi++] = b[j++];
}
int find(int ro,int goa)
{
int l = st[ro], r = en[ro] + 1;
while(l < r)
{
int mid = (l + r + 1) >> 1;
if(kk[mid].val <= goa) l = mid;
else r = mid - 1;
}
return l;
}
int query(int l,int r,int x)
{
int L = l >> K;
int R = r >> K;
if(L + 1 >= R)
{
vector <int> o;
for(int i = l;i <= r;i++)
o.push_back(k[i].val + seg[i>>K]);
sort(o.begin(),o.end());
return o[x-1];
}
int la = 1,ra = 1e7 + 1;
int ans = 0;
while(la <= ra)
{
int mid = (la + ra) / 2;
int cnt = 0;
for(int i = l;i <= en[L];i++)
{
if(k[i].val + seg[L] <= mid) cnt++;
}
for(int i = st[R];i <= r;i++)
{
if(k[i].val + seg[R] <= mid) cnt++;
}
for(int i = L + 1;i <= R - 1;i++)
{
if(kk[st[i]].val + seg[i] > mid) continue;
if(kk[en[i]].val + seg[i] <= mid)
{
cnt += en[i] - st[i] + 1;
continue;
}
int poi = find(i,mid - seg[i]);
cnt += poi - st[i] + 1;
}
if(cnt < x) la = mid + 1 ; //
else
{
ra = mid - 1; //
ans = mid;
}
}
return ans;
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
cin>>T;
while(T--)
{
cin>>n>>m;
for(int i=n;i>=1;i--) st[i>>K] = i;
for(int i=1;i<=n;i++) en[i>>K] = i,seg[i>>K] = 0;
for(int i=1;i<=n;i++)
{
cin>>k[i].val;
k[i].ord = i;
kk[i].val = k[i].val;
kk[i].ord = k[i].ord;
}
for(int i=0;i<=(n>>K);i++) sort(kk+st[i],kk+en[i]+1,cmp);
// for(int i=1;i<=n;i++) cout<<kk[i].val<<" "<<kk[i].ord<<'\n';
for(int i=1;i<=m;i++)
{
cin>>op>>ll>>rr>>s;
if(op == 1)
{
add(ll,rr,s);
// for(int j=1;j<=n;j++) cout<<k[j].val+seg[j>>K]<<' ';
// cout<<'\n';
}
else
{
cout<<query(ll,rr,s)<<"\n";
}
}
}
return 0;
}
3.子序列(涉及多维后缀和)

解法1:

解法2:分块 + 多维前缀和
这里g的初始化应该是对块前的所有数字计数,然后计算一下


解法3:


浙公网安备 33010602011771号