P7882 [Ynoi2006] rsrams 题解
大常数选手
若一个区间有贡献,那么该区间众数出现次数大于区间长度的一半。考虑根号分治,设阈值为
- 长度
\le2B 的区间:暴力找到合法区间及其贡献,变为二维数点,使用O(1) 单点加、O(\sqrt{n}) 查询的分块可以做到nB+m\sqrt{n} 。 - 长度
>2B 的区间:众数出现次数>B ,这样的数只有\frac{n}{B} 种。考虑逐个数算答案,令等于当前数的位置权值为1 ,否则为-1 ,前缀和后相当于数w[r]-w[l-1]>0 。区间逆序对使用莫队二次离线可以做到O(\frac{n^{2}}{B}\sqrt{m}) (由于|w[i]-w[i-1]|=1 ,不需要二离,普通莫队即可)
取
发现所有数出现的总次数为
设
注意上面所分析的阈值/块长忽略了常数,xjb 调一调能得到更好的表现
代码非常丑陋
#include <bits/stdc++.h>
#include <bits/extc++.h>
using namespace std; using namespace __gnu_pbds; using namespace __gnu_cxx;
#define For(i,x,y,...) for(int i=x,##__VA_ARGS__;i<=(y);++i)
#define rFor(i,x,y,...) for(int i=x,##__VA_ARGS__;i>=(y);--i)
#define Rep(i,x,y,...) for(int i=x,##__VA_ARGS__;i<(y);++i)
#define pb emplace_back
#define sz(a) int((a).size())
#define all(a) begin(a),end(a)
#define fi first
#define se second
typedef long long LL; typedef vector<int> Vi;
auto ckmax=[](auto &x,auto y) { return x<y ? x=y,true : false; };
auto ckmin=[](auto &x,auto y) { return y<x ? x=y,true : false; };
sfmt19937 mt(chrono::steady_clock::now().time_since_epoch().count());
int rnd(int l,int r) { return uniform_int_distribution<>(l,r)(mt); }
#define endl '\n'
namespace IO { const int L = 1<<25; char a[L],*l=a,*r=a,b[L],*p=b;
auto gc=[]() { if(l==r)r=(l=a)+fread(a,1,L,stdin);return l==r?EOF:*l++; };
auto pc=[](char x) { if(p-b==L)fwrite(b,1,L,stdout),p=b;*p++=x; };
struct IO {
~IO() { fwrite(b,1,p-b,stdout); }
template<typename T>IO& operator >> (T &x) {
x=0;bool f=1;char c;while(!isdigit(c=gc()))f&=c!='-';
if(f)do x=x*10+c-48;while(isdigit(c=gc()));
else do x=x*10-c+48;while(isdigit(c=gc()));
return *this;
}
IO& operator << (char x) { pc(x); return *this; }
IO& operator << (LL x) {
static char s[22];
if(!x)pc(48);
else{char *t=s;do *t++=x%10;while(x/=10);while(s!=t)pc(*--t|48);}
return *this;
}
} io; } using IO::io;
template<typename T=int>T read() { T x; io>>x; return x; }
const int N = 1e6+5;
int n,q,lim,a[N],ql[N],qr[N],tot[N],siz[N],sa[N];
LL ans[N];
namespace part1 {
int mx,cnt[N];
Vi q[N];
struct Block {
static const int B = 1e3, BN = N/B+5;
int in[N],le[BN],a[N]; LL tot,s[BN];
void init() {
For(i,1,n) in[i] = (i-1)/B+1;
le[1] = 1; For(i,2,in[n]) le[i] = le[i-1]+B;
}
void add(int i,int x) { a[i] += x, s[in[i]] += x, tot += x; }
LL sum(int r) {
int rr = in[r]; LL res = 0;
Rep(i,1,rr) res += s[i];
For(i,le[rr],r) res += a[i];
return res;
}
} blk;
void main() {
blk.init();
For(i,1,::q) q[qr[i]].pb(i);
For(r,1,n) {
int ll = max(1,r-2*lim+1), l = r;
for(; l >= ll; --l) {
if( tot[a[l]] <= lim ) {
cnt[a[l]] += 2;
if( cnt[a[l]] > cnt[mx] ) mx = a[l];
}
if( cnt[mx] > r-l+1 ) blk.add(l,mx);
else if( cnt[mx]+2*(l-ll) <= r-ll+1 ) { --l; break; }
}
mx = 0; while( l < r ) cnt[a[++l]] -= 2;
for(int i : q[r]) ans[i] += blk.tot-blk.sum(ql[i]-1);
}
cerr<<"part1: "<<clock()/1e6<<"s\n";
}
}
namespace part2 {
int m,q,p[N],w[N],le[N],ri[N],in[N],sa[N],ql[N],qr[N],id[N],ds[N*2];
bitset<N> vis;
void _sort() {
static int buc[N];
For(i,1,m, b = max(2*m/sqrt(q),1.)) in[i] = (i-1)/b+1;
memset(buc+1,0,sizeof(int)*m);
For(i,1,q) ++buc[in[ql[i]]]; For(i,1,m) buc[i] += buc[i-1];
rFor(i,q,1) id[buc[in[ql[i]]]--] = i;
// Rep(i,1,q)
// if( in[ql[id[i]]] == in[ql[id[i+1]]] ) assert(qr[id[i]]<=qr[id[i+1]]);
// else assert(in[ql[id[i]]]<in[ql[id[i+1]]]);
For(i,1,q) if( !(in[ql[id[i]]] & 1) ) {
int j = i; while( j < q && in[ql[id[j]]] == in[ql[id[j+1]]] ) ++j;
reverse(id+i,id+j+1), i = j;
}
}
void mo(int x) {
m = q = 0, vis.reset();
For(i,1,n, j = 0)
if( a[i] == x ) vis[i] = 1, ++j;
else if( j ) vis[i] = 1, --j;
rFor(i,n,1, j = 0)
if( a[i] == x ) ++j;
else if( j ) vis[i] = 1, --j;
For(i,1,n) if( vis[i] ) p[++m] = i;
// For(i,1,m) assert(abs(w[i]-w[i-1])==1);
w[0] = m; For(i,1,m) w[i] = a[p[i]]==x ? w[i-1]+1 : w[i-1]-1;
Rep(i,1,m) {
le[p[i]] = ri[p[i]] = i;
Rep(j,p[i]+1,p[i+1]) le[j] = i, ri[j] = i+1;
} le[p[m]] = ri[p[m]] = m;
For(i,1,::q) {
int l = ::ql[::sa[i]], r = ::qr[::sa[i]];
if( r < p[1] || p[m] < l ) continue;
sa[++q] = ::sa[i], ql[q] = ri[max(l,p[1])], qr[q] = le[min(r,p[m])];
if( ql[q] > qr[q] ) --q;
}
_sort();
int T=0; LL now = 0, pre = 0, suf = 0;
memset(ds+1,0,sizeof(int)*2*m);
For(i,1,q, l = 1, r = 0, lp = m, rp = m) {
int j = id[i];
// T += abs(r-qr[j])+abs(l-ql[j]);
while( r < qr[j] )
rp<w[++r] ? pre+=ds[rp++] : pre-=ds[--rp],
now += (w[l-1]<w[r])+pre, ++ds[rp], suf += lp<rp;
while( l > ql[j] )
++ds[lp], pre += lp<rp,
lp>w[(--l)-1] ? suf+=ds[lp--] : suf-=ds[++lp], now += suf;
while( r > qr[j] )
now -= (w[l-1]<w[r])+pre, --ds[rp], suf -= lp<rp,
rp<w[--r] ? pre+=ds[rp++] : pre-=ds[--rp];
while( l < ql[j] )
now -= suf, lp>w[l++] ? suf+=ds[lp--] : suf-=ds[++lp],
--ds[lp], pre -= lp<rp;
// assert(lp==w[l-1]), assert(rp==w[r]),
ans[sa[j]] += x * now;
}
// cerr<<T<<' '<<int(m*sqrt(q))<<endl;
}
void main() {
int T=0;
For(i,1,n) if( tot[i] > lim ) ++T, mo(i);
cerr<<"part2: "<<T<<endl;
}
}
signed main() {
io>>n>>q; For(i,1,n) ++tot[a[i]=read()];
For(i,1,n) ++siz[tot[i]]; For(i,1,q) io>>ql[i]>>qr[i];
for(lim = 1000; !siz[lim]; --lim);
cerr<<"lim = "<<lim<<endl;
iota(sa+1,sa+q+1,1), sort(sa+1,sa+q+1,[](int x,int y){return qr[x]<qr[y];});
part1::main(), part2::main();
For(i,1,q) io<<ans[i]<<endl;
return 0;
}
upd:莫队奇偶优化那里边界挂了