P4690 Ynoi2016 镜中的昆虫
P4690 Ynoi2016 镜中的昆虫
原题不会见祖宗。
前置
珂朵莉树、cdq 分治、树状数组
思路
单点修改区间查询
定义
每次修改时就相当于修改四个同颜色的点的
但实际上也可以使用 cdq 分治,先按照时间排序,对于时间区间
然后按照修改的
但这样我们的 cdq 分治的排序会比较复杂,需要维护查询
我们可以考虑把查询和修改拆开拆成两个数组,
inline void solve(int l1,int r1,int l2,int r2,int L,int R)
{
if(l1==r1||l2==r2||L==R) return ;
int mid=(L+R)>>1;
int mid1=l1;while(mid1<r1&&md[mid1+1].t<=mid) mid1++;
int mid2=l2;while(mid2<r2&&qr[mid2+1].t<=mid) mid2++;
solve(l1,mid1,l2,mid2,L,mid);solve(mid1,r1,mid2,r2,mid,R);
if(l1!=mid1&&mid2!=r2)
{
sort(md+l1+1,md+mid1+1);sort(qr+mid2+1,qr+r2+1);
for(int i=mid2+1,j=l1+1;i<=r2;i++)
{
while(j<=mid1&&md[j].pre<qr[i].l) T.updata(md[j].pos,md[j].val),j++;
qr[i].ans+=T.getsum(qr[i].r)-T.getsum(qr[i].l-1);
}
for(int i=l1+1;i<=mid1;i++) T.del(md[i].pos);
}
}
//md 是修改,qr 是查询
区间修改区间查询
结论:对一个长度为 n 的数组进行 m 次区间赋值操作,pre 数组的改变次数为 级别
我们只需要每次对于一个区间推平
根据这个性质,我们可以把颜色相同的区间看做一个点,所以
具体的修改可以参考珂朵莉树的区间推平,一个 set
维护所有的区间和颜色。set
维护,注意每个颜色的 set
和整体的 set
是同步的。
具体看代码吧。
CODE
#include<bits/stdc++.h>
using namespace std;
const int maxn=1e5+5;
int n,m;
int a[maxn];
int pre[maxn],npre[maxn];
int tp[maxn],lf[maxn],rt[maxn],co[maxn];
#define SNI set <nod> :: iterator
#define SDI set <data> :: iterator
struct modi{int t,pos,pre,val;friend bool operator<(modi a,modi b){return a.pre<b.pre;}}md[10*maxn];int tp1;
struct qry{int t,l,r,ans;friend bool operator<(qry a,qry b){return a.l<b.l;}}qr[maxn];int tp2,cnt;
inline bool cmp(qry a,qry b){return a.t<b.t;}
inline void modify(int pos,int co)//将 pos 点的 pre 值改成 co(原来的 pre 值是 npre[pos])
{
if(npre[pos]==co) return ;
md[++tp1]=(modi){++cnt,pos,npre[pos],-1};
md[++tp1]=(modi){++cnt,pos,npre[pos]=co,1};
}
namespace preview
{
int lst[2*maxn];
map<int,int>mp;
inline void prew()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++) scanf("%d",&a[i]),mp[a[i]]=1;
for(int i=1;i<=m;i++)
{
scanf("%d%d%d",&tp[i],&lf[i],&rt[i]);
if(tp[i]==1) scanf("%d",&co[i]);
mp[co[i]]=1;
}
map<int,int>::iterator it,it1;//离散化
for(it=mp.begin(),it1=it,it1++;it1!=mp.end();it1++,it++) it1->second+=it->second;
for(int i=1;i<=n;i++) a[i]=mp[a[i]];for(int i=1;i<=m;i++) co[i]=mp[co[i]];
for(int i=1;i<=n;i++) pre[i]=lst[a[i]],lst[a[i]]=i;
for(int i=1;i<=n;i++) npre[i]=pre[i];
}
}
namespace colist//区间修改
{
struct data {int l,r,x;friend bool operator<(data a,data b){return a.r<b.r;}};set <data> s;
struct nod {int l,r;friend bool operator<(nod a,nod b){return a.r<b.r;}};set <nod> c[2*maxn];set <int> bd;
//bd 维护了等待更改的 l 点
inline void split(int mid)//把区间 [l,r] 拆成 [l,mid-1] 和 [mid,r]
{
SDI it=s.lower_bound((data){0,mid,0});data p=*it;
if(mid==p.r) return ;
s.erase(p);s.insert((data){p.l,mid,p.x});s.insert((data){mid+1,p.r,p.x});
c[p.x].erase((nod){p.l,p.r});c[p.x].insert((nod){p.l,mid});c[p.x].insert((nod){mid+1,p.r});
}
inline void del(set <data> :: iterator it)//删除一个区间
{
bd.insert(it->l);SNI it1,it2;
it1=it2=c[it->x].find((nod){it->l,it->r});
it2++;if(it2!=c[it->x].end()) bd.insert(it2->l);
c[it->x].erase(it1);
s.erase(it);
}
inline void ins(data p)//加入一个区间
{
s.insert(p);
SNI it=c[p.x].insert((nod){p.l,p.r}).first;
it++; if(it!=c[p.x].end()) bd.insert(it->l);
}
inline void stv(int l,int r,int x)
{
if(l!=1) split(l-1);split(r);int p=l;//
while(p!=r+1){SDI it=s.lower_bound((data){0,p,0});p=it->r+1;del(it);}
ins((data){l,r,x});
for(auto it=bd.begin();it!=bd.end();it++)//对 l 点 pre 进行更新
{
auto it1=s.lower_bound((data){0,*it,0});
if(*it!=it1->l) modify(*it,*it-1);
else
{
auto it2=c[it1->x].lower_bound((nod){0,*it});
if(it2!=c[it1->x].begin()) it2--,modify(*it,it2->r);
else modify(*it,0);
}
}
bd.clear();
}
inline void ih()
{
int nc=a[1];int ccnt=1;
for(int i=2;i<=n;i++)
if(nc!=a[i]){s.insert((data){i-ccnt,i-1,nc});c[nc].insert((nod){i-ccnt,i-1});nc=a[i],ccnt=1;}
else ccnt++;
s.insert((data){n-ccnt+1,n,nc});c[a[n]].insert((nod){n-ccnt+1,n});
}
}
namespace cdq
{
struct treearray//树状数组
{
const int K=maxn-5;
int tree[maxn];
int lowbit(int x){return x&(-x);}
void updata(int x,int y){for(;x<=K;x+=lowbit(x)) tree[x]+=y;}
void del(int x){for(;x<=K;x+=lowbit(x)) tree[x]=0;}
int getsum(int x){int sum=0;for(;x;x-=lowbit(x)) sum+=tree[x];return sum;}
void clr(){for(int x=1;x<=K;x++) tree[x]=0;}
}T;
int srt[maxn];
inline bool cmp1(int a,int b){return pre[a]<pre[b];}
inline void solve(int l1,int r1,int l2,int r2,int L,int R)//cdq 分治
{
if(l1==r1||l2==r2||L==R) return ;
int mid=(L+R)>>1;
int mid1=l1;while(mid1<r1&&md[mid1+1].t<=mid) mid1++;
int mid2=l2;while(mid2<r2&&qr[mid2+1].t<=mid) mid2++;
solve(l1,mid1,l2,mid2,L,mid);solve(mid1,r1,mid2,r2,mid,R);
if(l1!=mid1&&mid2!=r2)
{
sort(md+l1+1,md+mid1+1);sort(qr+mid2+1,qr+r2+1);
for(int i=mid2+1,j=l1+1;i<=r2;i++)
{
while(j<=mid1&&md[j].pre<qr[i].l) T.updata(md[j].pos,md[j].val),j++;
qr[i].ans+=T.getsum(qr[i].r)-T.getsum(qr[i].l-1);
}
for(int i=l1+1;i<=mid1;i++) T.del(md[i].pos);
}
}
inline void mainsolve()
{
colist::ih();
for(int i=1;i<=m;i++)
if(tp[i]==1) colist::stv(lf[i],rt[i],co[i]);
else qr[++tp2]=(qry){++cnt,lf[i],rt[i],0};
sort(qr+1,qr+tp2+1);for(int i=1;i<=n;i++) srt[i]=i;sort(srt+1,srt+n+1,cmp1);
for(int i=1,j=1;i<=tp2;i++)//对于初始的颜色不进行 cdq
{
while(j<=n&&pre[srt[j]]<qr[i].l) T.updata(srt[j],1),j++;
qr[i].ans+=T.getsum(qr[i].r)-T.getsum(qr[i].l-1);
}
T.clr();
//cdq 中的贡献一定是修改给予的
sort(qr+1,qr+tp2+1,cmp);solve(0,tp1,0,tp2,0,cnt);sort(qr+1,qr+tp2+1,cmp);
for(int i=1;i<=tp2;i++) printf("%d\n",qr[i].ans);
}
}
int main()
{
preview::prew();cdq::mainsolve();
}
分类:
trick / 区间单一颜色
, 分治 / cdq 分治
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现