2024-03-31
2024-03-31
讲课提到的
很有道理啊,确实很常见
在窗口的星星里面就用到了
还有一个小技巧
求区间 0 的个数不好做
有的时候满足所有数非负
转化成求区间最小值是不是 0 和区间最小值的个数就行了
这两天讲课的时候还经常提到
· 修改和查询的复杂度不平衡的时候,把他平衡会更优秀
扫描线:将静态二维问题转化为动态一维问题
窗口的星星
每个星星可以转化为 横坐标从 x 到 x+W-1 ,纵坐标从 y 到 y+H-1 的 权值为 l 的矩形
这样我们要求的就变成了二维平面上权值最大的点
扫描线,每次区间加 取全局最大值
注意这个题和模板题求面积不同,那个题线段树维护的是切出来的线段,点是边界,这个题线段树维护的就是点,因此不用有 lft+1 之类的操作
#include<iostream>
#include<cstring>
#include<algorithm>
#include<vector>
#define ls (u<<1)
#define rs (u<<1|1)
#define mid (tr[u].lft+tr[u].rgh>>1)
using namespace std;
typedef long long ll;
const int N=1e4+100;
int n;
ll W,H;
struct Seg {
ll pos;
ll hgh,low;
ll wgh;
bool operator <(const Seg &t)const {
if(pos==t.pos) return wgh>t.wgh;
return pos<t.pos;
}
}a[N*2];
int cnt;
struct Node {
int lft,rgh;
ll val,tag;
}tr[N*8];
vector<ll> nums;
ll g(ll x) {
return lower_bound(nums.begin(),nums.end(),x)-nums.begin();
}
void build(int u,int lft,int rgh) {
tr[u].rgh=rgh,tr[u].lft=lft;
tr[u].tag=tr[u].val=0;
if(lft==rgh) return;
build(ls,lft,mid),build(rs,mid+1,rgh);
}
void pushup(int u) {
tr[u].val=max(tr[ls].val,tr[rs].val);
}
void pushdown(int u) {
if(tr[u].tag) {
tr[ls].val+=tr[u].tag,tr[rs].val+=tr[u].tag;
tr[ls].tag+=tr[u].tag,tr[rs].tag+=tr[u].tag;
tr[u].tag=0;
}
}
void update(int u,int ul,int ur,ll ux) {
if(tr[u].lft>=ul&&tr[u].rgh<=ur) {
tr[u].val+=ux,tr[u].tag+=ux;
return;
}
pushdown(u);
if(ul<=mid) update(ls,ul,ur,ux);
if(ur>mid) update(rs,ul,ur,ux);
pushup(u);
}
int main() {
int T;
scanf("%d",&T);
while(T--) {
memset(a,0,sizeof(a));
memset(tr,0,sizeof(tr));
cnt=0;
nums.clear();
scanf("%d%lld%lld",&n,&W,&H);
for(int i=1;i<=n;i++) {
ll x,y,w;
scanf("%lld%lld%lld",&x,&y,&w);
nums.push_back(y),nums.push_back(y+H-1);
cnt++,a[cnt].pos=x,a[cnt].low=y,a[cnt].hgh=y+H-1,a[cnt].wgh=w;
cnt++,a[cnt].pos=x+W-1,a[cnt].low=y,a[cnt].hgh=y+H-1,a[cnt].wgh=-w;
}
sort(nums.begin(),nums.end());
nums.erase(unique(nums.begin(),nums.end()),nums.end());
sort(a+1,a+cnt+1);
ll ans=0;
build(1,1,nums.size());
for(int i=1;i<=cnt;i++) {
update(1,g(a[i].low),g(a[i].hgh),a[i].wgh);
ans=max(ans,tr[1].val);
}
printf("%lld\n",ans);
}
return 0;
}
Yuno loves sqrt technology III
好像是做过的第一道 Ynoi 的题
强制在线的区间众数的出现次数
分块
先离散化
预处理
f[i][j]
表示 块 i 到 块 j 的众数(用一个桶)- 一个 vector
pos[N]
表示每种数的位置,并记录每个元素在 vector 中的下标idx
处理询问的时候
- 整块通过 f 数组求
- 散块暴力扩展,一个元素
w[i]
若pos[w[i]][idx[i]+ans]<=qr
说明共有 ans+1 个这种数出现,答案可以加以(这是对于左边的散块来说,右边差不多)
时间复杂度
#include<iostream>
#include<cstring>
#include<algorithm>
#include<vector>
#include<cmath>
using namespace std;
const int N=5e5+50,B=777;
const int Inf=2e9;
int n,m;
int w[N];
vector<int> nums;
vector<int> pos[N];
int idx[N];
int len,blk[N],lft[B],rgh[B];
int f[B][B];
int cnt[N];
int g(int x) {
return lower_bound(nums.begin(),nums.end(),x)-nums.begin()+1;
}
int main() {
scanf("%d%d",&n,&m);
int len=sqrt(n);
for(int i=1;i<=n;i++) blk[i]=(i-1)/len+1,lft[blk[i]]=Inf,rgh[blk[i]]=-Inf;
for(int i=1;i<=n;i++) lft[blk[i]]=min(lft[blk[i]],i),rgh[blk[i]]=max(rgh[blk[i]],i);
for(int i=1;i<=n;i++) scanf("%d",&w[i]),nums.push_back(w[i]);
sort(nums.begin(),nums.end());
nums.erase(unique(nums.begin(),nums.end()),nums.end());
for(int i=1;i<=n;i++) w[i]=g(w[i]);
for(int i=1;i<=blk[n];i++) {
for(int j=i;j<=blk[n];j++) {
f[i][j]=f[i][j-1];
for(int k=lft[j];k<=rgh[j];k++) f[i][j]=max(f[i][j],++cnt[w[k]]);
}
memset(cnt,0,sizeof(cnt));
}
for(int i=1;i<=n;i++) pos[w[i]].push_back(i),idx[i]=pos[w[i]].size()-1;
int ans=0;
while(m--) {
int ql,qr;
scanf("%d%d",&ql,&qr);
ql^=ans,qr^=ans;
ans=0;
if(blk[ql]==blk[qr]) {
for(int i=ql;i<=qr;i++) ans=max(ans,++cnt[w[i]]);
for(int i=ql;i<=qr;i++) cnt[w[i]]--;
}
else {
ans=f[blk[ql]+1][blk[qr]-1];
for(int i=ql;i<=rgh[blk[ql]];i++)
while(idx[i]+ans<pos[w[i]].size()&&pos[w[i]][idx[i]+ans]<=qr)
ans++;
for(int i=qr;i>=lft[blk[qr]];i--)
while(idx[i]-ans>=0&&pos[w[i]][idx[i]-ans]>=ql)
ans++;
}
printf("%d\n",ans);
}
return 0;
}
XOR and Favorite Number
异或可以前缀和
问题转化为
直接莫队
cnt
记录数们出现的次数
加入的时候看有多少个 cnt[x^k]
res 就加多少
删除差不多
注意 两个数异或起来可能会比原来的两个数都大,空间要开够
#include<iostream>
#include<cstring>
#include<algorithm>
#include<cmath>
using namespace std;
typedef long long ll;
const int N=100100,V=2000100;
int n,m,k;
int w[N];
int len;
struct Query {
int id;
int l,r;
#define blk(x) ((x-1)/len+1)
bool operator< (const Query &t)const {
if(blk(l)==blk(t.l)) return r<t.r;
return blk(l)<blk(t.l);
}
}qry[N];
ll ans[N];
ll res;
ll cnt[V];
void add(int x) {
res+=cnt[x^k];
cnt[x]++;
}
void del(int x) {
cnt[x]--;
res-=cnt[x^k];
}
int main() {
scanf("%d%d%d",&n,&m,&k);
len=sqrt(n);
for(int i=1;i<=n;i++) scanf("%d",&w[i]),w[i]^=w[i-1];
for(int i=1;i<=m;i++) {
scanf("%d%d",&qry[i].l,&qry[i].r);
qry[i].id=i;
}
sort(qry+1,qry+m+1);
int h=0,t=1;
for(int i=1;i<=m;i++) {
int l=qry[i].l-1,r=qry[i].r;
while(h<r) add(w[++h]);
while(t>l) add(w[--t]);
while(h>r) del(w[h--]);
while(t<l) del(w[t++]);
ans[qry[i].id]=res;
}
for(int i=1;i<=m;i++) printf("%lld\n",ans[i]);
return 0;
}
教主的魔法
分块
块内维护排序后的序列,还要记录排完序之后的位置
- 修改:
- 整块打标记
- 散块暴力加,重构
- 查询:
- 整块二分
- 散块暴力统计
查询的时候记得处理 tag (散块当然也要处理)数据太弱这都能得 90 分,很久没看出来
#include<iostream>
#include<cstring>
#include<algorithm>
#include<cmath>
using namespace std;
const int N=1e6+100,B=1010;
const int Inf=2e9;
int n,m;
struct Data {
int val;
int id;
bool operator< (const Data &t)const {
return val<t.val;
}
}w[N];
int len,blk[N],lft[B],rgh[B],tag[B],pos[N];
void update(int l,int r,int x) {
if(blk[l]==blk[r]) {
for(int i=l;i<=r;i++) w[pos[i]].val+=x;
sort(w+lft[blk[l]],w+rgh[blk[l]]+1);
for(int i=lft[blk[l]];i<=rgh[blk[l]];i++) pos[w[i].id]=i;
return;
}
for(int i=blk[l]+1;i<=blk[r]-1;i++) tag[i]+=x;
for(int i=l;i<=rgh[blk[l]];i++) w[pos[i]].val+=x;
sort(w+lft[blk[l]],w+rgh[blk[l]]+1);
for(int i=lft[blk[l]];i<=rgh[blk[l]];i++) pos[w[i].id]=i;
for(int i=lft[blk[r]];i<=r;i++) w[pos[i]].val+=x;
sort(w+lft[blk[r]],w+rgh[blk[r]]+1);
for(int i=lft[blk[r]];i<=rgh[blk[r]];i++) pos[w[i].id]=i;
}
int lwrbnd(int lft,int rgh,int x) {
while(lft<rgh) {
int mid=lft+rgh>>1;
if(w[mid].val>=x) rgh=mid;
else lft=mid+1;
}
return lft;
}
int query(int l,int r,int x) {
int res=0;
if(blk[l]==blk[r]) {
for(int i=l;i<=r;i++) if(w[pos[i]].val<x-tag[blk[l]]) res++;
return r-l+1-res;
}
for(int i=blk[l]+1;i<=blk[r]-1;i++) res+=lwrbnd(lft[i],rgh[i]+1,x-tag[i])-lft[i];
for(int i=l;i<=rgh[blk[l]];i++) if(w[pos[i]].val<x-tag[blk[l]]) res++;
for(int i=lft[blk[r]];i<=r;i++) if(w[pos[i]].val<x-tag[blk[r]]) res++;
return r-l+1-res;
}
int main() {
scanf("%d%d",&n,&m);
len=sqrt(n);
for(int i=1;i<=n;i++) blk[i]=(i-1)/len+1,lft[blk[i]]=Inf,rgh[blk[i]]=-Inf;
for(int i=1;i<=n;i++) lft[blk[i]]=min(lft[blk[i]],i),rgh[blk[i]]=max(rgh[blk[i]],i);
for(int i=1;i<=n;i++) scanf("%d",&w[i].val),w[i].id=i;
for(int i=1;i<=blk[n];i++) {
sort(w+lft[i],w+rgh[i]+1);
for(int j=lft[i];j<=rgh[i];j++) pos[w[j].id]=j;
}
while(m--) {
char opt[5];
int l,r,x;
scanf("%s%d%d%d",opt,&l,&r,&x);
if(*opt=='M') update(l,r,x);
else printf("%d\n",query(l,r,x));
}
return 0;
}
作业
莫队+值域分块
明天再写
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· winform 绘制太阳,地球,月球 运作规律
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· 写一个简单的SQL生成工具
· AI 智能体引爆开源社区「GitHub 热点速览」