专栏文章
题解:P13834 【MX-X18-T6】「FAOI-R6」Voices of the Chord
P13834题解参与者 1已保存评论 0
文章操作
快速查看文章及其快照的属性,并进行相关操作。
- 当前评论
- 0 条
- 当前快照
- 1 份
- 快照标识符
- @min9jkvi
- 此快照首次捕获于
- 2025/12/01 22:47 3 个月前
- 此快照最后确认于
- 2025/12/01 22:47 3 个月前
闲话
这不是带权分块模板题?
题解
发现这个东西没什么性质,不太有低于根号的做法,所以考虑分块。
首先将 数组分块,对于每一个块存 表示第 个块中 数组前 个数出现了多少次,再记一个 表示加法标记,然后每次询问遍历所有块,累加 乘上 ,就做完了整块修改对于询问的贡献。
注意 的处理要在块内去重,因为我的 可以相同,全放 的最大值就炸了,而去重之后最多也就是 的总和。
发现 的散块也不好做,因为 并没有总和的限制,于是将 也分块。
对于每一个 的块,你不难再记一个前缀和 表示第 块在 的前 位出现了几次。这是简单的,因为 的总和一定,可以先记一个桶 表示块内在 集合中出现了几次,然后跑一次前缀和即可。
这样 对 的整块都可以算了,自然可以算 的散块对 的整块的贡献。
但是 的散块对 的散块的贡献还是算不了,因为 的散块可以出现在 中相当多次。
于是考虑带权分块,一样的分成大约 份,散块就可以暴力跑了。
复杂度精细实现后为 ,最优块长没有意义,反正要卡常。
代码
注意到 ,理论块长 最优,但实际上块长要开到三百到两千都跑的超快,差的也太离谱了。
下面的码风丑到极致,看一下怎么做就行了。
CPP#include<bits/stdc++.h>
#define int unsigned int
#define lowbit(x) (x&(-x))
#define max(x,y) (((x)>(y))?(x):(y))
#define min(x,y) (((x)<(y))?(x):(y))
namespace fast_IO{
#define IOSIZE 200000
char ibuf[IOSIZE], obuf[IOSIZE], *p1 = ibuf, *p2 = ibuf, *p3 = obuf;
#define getchar() ((p1==p2)and(p2=(p1=ibuf)+fread(ibuf,1,IOSIZE,stdin),p1==p2)?(EOF):(*p1++))
#define putchar(x) ((p3==obuf+IOSIZE)&&(fwrite(obuf,p3-obuf,1,stdout),p3=obuf),*p3++=x)
#define isdigit(ch) (ch>47&&ch<58)
#define isspace(ch) (ch<33)
template<typename T> inline T read() { T s = 0; int w = 1; char ch; while (ch = getchar(), !isdigit(ch) and (ch != EOF)) if (ch == '-') w = -1; if (ch == EOF) return false; while (isdigit(ch)) s = s * 10 + ch - 48, ch = getchar(); return s * w; }
template<typename T> inline bool read(T &s) { s = 0; int w = 1; char ch; while (ch = getchar(), !isdigit(ch) and (ch != EOF)) if (ch == '-') w = -1; if (ch == EOF) return false; while (isdigit(ch)) s = s * 10 + ch - 48, ch = getchar(); return s *= w, true; }
template<typename T> inline void print(T x) { if (x < 0) putchar('-'), x = -x; if (x > 9) print(x / 10); putchar(x % 10 + 48); }
inline bool read(char &s) { while (s = getchar(), isspace(s)); return true; }
inline bool read(char *s) { char ch; while (ch = getchar(), isspace(ch)); if (ch == EOF) return false; while (!isspace(ch)) *s++ = ch, ch = getchar(); *s = '\000'; return true; }
inline void print(char x) { putchar(x); }
inline void print(char *x) { while (*x) putchar(*x++); }
inline void print(const char *x) { for (int i = 0; x[i]; i++) putchar(x[i]); }
inline bool read(std::string& s) { s = ""; char ch; while (ch = getchar(), isspace(ch)); if (ch == EOF) return false; while (!isspace(ch)) s += ch, ch = getchar(); return true; }
inline void print(std::string x) { for (int i = 0, n = x.size(); i < n; i++) putchar(x[i]); }
inline bool read(bool &b) { char ch; while(ch=getchar(), isspace(ch)); b=ch^48; return true; }
inline void print(bool b) { putchar(b+48); }
template<typename T, typename... T1> inline int read(T& a, T1&... other) { return read(a) + read(other...); }
template<typename T, typename... T1> inline void print(T a, T1... other) { print(a), print(other...); }
struct Fast_IO { ~Fast_IO() { fwrite(obuf, p3 - obuf, 1, stdout); } } io;
template<typename T> Fast_IO& operator >> (Fast_IO &io, T &b) { return read(b), io; }
template<typename T> Fast_IO& operator << (Fast_IO &io, T b) { return print(b), io; }
#define cout io
#define cin io
#define endl '\n'
} using namespace fast_IO;
//#define int long long
/*
不是,歌词里面有断章取义的敏感词导致没法保存?
这不是注释吗?
*/
using namespace std;
int a[1000001];
const int S=300,B=800,N=100000;
int BL[1000001],BR[1000001],bel[1000001];
int n,k,m,q,b[1000001],c[1000001],Bel[1000001],L[1000001],R[1000001],suf[505][N+5],sum[1000001],h[1000001];
int tag[1000001];
vector<int>e[N+5],g[N+5];
int cnt[1000001],pre[405][N+5],cc[505][N+5];
void update(int l,int r,int x){
if(bel[l]==bel[r]){
for(int i=l;i<=r;i++)h[b[i]]+=x;
for(int i=1;i<=Bel[n];i++){
sum[i]+=x*(suf[i][r]-suf[i][l-1]);
}
}else{
for(int i=l;i<=R[bel[l]];i++)h[b[i]]+=x;
for(int i=1;i<=Bel[n];i++)sum[i]+=x*(suf[i][R[bel[l]]]-suf[i][l-1]);
for(int i=L[bel[r]];i<=r;i++)h[b[i]]+=x;
for(int i=1;i<=Bel[n];i++)sum[i]+=x*(suf[i][r]-suf[i][L[bel[r]]-1]);
for(int i=bel[l]+1;i<=bel[r]-1;i++)tag[i]+=x;
}
}
int work(int posl,int posr){
// printf(",5t5 %d %d\n",posl,posr);
int ans=0;
for(int i=posl;i<=posr;i++)ans+=sum[i];
return ans;
}
int query(int l,int r){
int ans=0;
for(int i=1;i<=bel[k];i++){
ans+=tag[i]*(pre[i][r]-pre[i][l-1]);
}
if(Bel[l]==Bel[r]){
if(l==BL[Bel[l]]&&r==BR[Bel[r]]){
ans+=work(Bel[l],Bel[r]);
}else{
for(int i=l;i<=r;i++){
for(int v:g[i])ans+=h[v];
}
}
}else{
int posl=Bel[l]+1,posr=Bel[r]-1;
if(r==BR[Bel[r]])posr++;
else{
for(int i=BL[Bel[r]];i<=r;i++)for(int v:g[i])ans+=h[v];
}
if(l==BL[Bel[l]])posl--;
else{
for(int i=l;i<=BR[Bel[l]];i++)for(int v:g[i])ans+=h[v];
}
// printf("%d ::\n",ans);
ans+=work(posl,posr);
}
return ans;
}
void Debug(){
printf("::::b 分成 %d 块:::\n",bel[k]);
for(int i=1;i<=bel[k];i++){
printf("第%d块: ",i);
for(int j=L[i];j<=R[i];j++)printf("%d ",j);
puts("");
for(int j=1;j<=n;j++){
printf("对前 %d 个数贡献为 %d\n",j,pre[i][j]);
}
puts("");
}
for(int i=1;i<=n;i++)printf("%d ",g[i].size());
printf("::::a 分成 %d 块:::\n",Bel[n]);
for(int i=1;i<=Bel[n];i++){
printf("第%d块: ",i);
for(int j=BL[i];j<=BR[i];j++)printf("%d ",j);
puts("");
for(int j=1;j<=k;j++){
printf("对前 %d 个数贡献为 %d\n",j,suf[i][j]);
}
puts("");
}
}
signed main(){
// freopen("dashuju.in","r",stdin);
cin>>n>>k>>m>>q;
for(int i=1;i<=n;i++)bel[i]=(i-1)/S+1;
for(int i=1;i<=k;i++){
cin>>b[i];
}
for(int i=1;i<=m;i++){
cin>>cnt[i];
int x;
for(int j=1;j<=cnt[i];j++){
cin>>x;
e[i].push_back(x);
g[x].push_back(i);
}
}
for(int i=1;i<=k;i++)bel[i]=(i-1)/S+1;
for(int i=1;i<=k;i++)R[bel[i]]=i;
for(int i=k;i>=1;i--)L[bel[i]]=i;
for(int i=1;i<=k;i++)c[i]=b[i];
for(int i=1;i<=bel[k];i++){
sort(c+L[i],c+1+R[i]);
int res=1;
for(int j=L[i]+1;j<=R[i];j++){
if(c[j]!=c[j-1]){
for(int v:e[c[j-1]]){
pre[i][v]+=res;
}
res=1;
}else res++;
}
for(int v:e[c[R[i]]]){
pre[i][v]+=res;
}
for(int j=1;j<=n;j++){
pre[i][j]+=pre[i][j-1];
}
} //这是第一部分的贡献
int res=0;
Bel[1]=1;
res=g[1].size();
for(int i=2;i<=n;i++){
res+=g[i].size();
Bel[i]=Bel[i-1];
if(res>=B){
res=g[i].size();
Bel[i]++;
}
}
for(int i=1;i<=n;i++)BR[Bel[i]]=i;
for(int i=n;i>=1;i--)BL[Bel[i]]=i;
for(int i=1;i<=n;i++){
for(int v:g[i]){
cc[Bel[i]][v]++;
}
}
for(int i=1;i<=Bel[n];i++){
for(int j=1;j<=k;j++){
suf[i][j]=suf[i][j-1]+cc[i][b[j]];
}
}
// Debug();
int lasans=0;
for(int i=1;i<=q;i++){
lasans%=65536;
int opt;
cin>>opt;
if(opt==1){
int l,r,x;
cin>>l>>r>>x;
l^=lasans;r^=lasans;
update(l,r,x);
}else{
int l,r;
cin>>l>>r;
l^=lasans;r^=lasans;
lasans=query(l,r);
cout<<lasans<<endl;
}
}
}
/*
10 10 2 4
1 2 1 1 1 1 1 1 2 1
9 3 7 1 9 6 8 7 3 2
9 1 6 6 7 6 8 5 10 10
1 8 9 8
1 5 10 3
1 6 6 2
2 4 5
a 带权分块 b 正常分块
整块 b:排序一下然后存 pre[i][j]表示第 i 个块对 a 的前 j 个数有多大贡献
散块 b 对整块 a:对整块存 suf[i][j] 表示前 i 个块对 b 的前 j 个数重了多少个。
tag是b的 sum是a的。
*/
相关推荐
评论
共 0 条评论,欢迎与作者交流。
正在加载评论...