CDQ 专题
前言中的前言
-
由于本人过菜,有些题解会咕掉,请原谅这个蒟蒻
-
由于本人过菜,不知道什么时候就
了,想给这个机房留下点什么…… -
如果想看高效进阶的题解,建议出门左拐,去云落那里看看,保证是全网最全最好的,但不要对云落的博客好奇,更不要看云落的一言 和 云落的合集:黑夜刀己,白日爱人
正片开始!
好耶!!!(泪目)
题面
前言
首先登场的是板子题,借这个题讲一下cdq
正文
准确来说,
好的就这道题而言,有三个属性(这里记为
我们先按
那么,一个数的左边的数第一维一定符合条件
好的,上面说了
把这个序列从中间分成两半,此时不管两个序列内部顺序怎么变,对右面的来说,左面的在
对每一半都按
维护两个指针:
维护后一半的指针
代码
#include<bits/stdc++.h>
using namespace std;
const int N=2e5+5;
int n,m,nn;
struct node{int x,y,z,ans,w;}a[N],b[N];
bool cmp(node a,node b){
if(a.x==b.x){
if(a.y==b.y)return a.z<b.z;
return a.y<b.y;
}
return a.x<b.x;
}
bool cmp2(node a,node b){
if(a.y==b.y)return a.z<b.z;
return a.y<b.y;
}
int c[N];
int lowbit(int x){return x&-x;}
void add(int x,int d){
for(;x<=m;x+=lowbit(x))c[x]+=d;
}
int query(int x){
int res=0;
for(;x;x-=lowbit(x))res+=c[x];
return res;
}
void cdq(int l,int r){
if(l==r)return;
int mid=(l+r)>>1;
cdq(l,mid),cdq(mid+1,r);
sort(a+l,a+mid+1,cmp2);
sort(a+mid+1,a+1+r,cmp2);
int i=mid+1,j=l;
while(i<=r){
while(a[j].y<=a[i].y&&j<=mid){
add(a[j].z,a[j].w);
j++;
}
a[i].ans+=query(a[i].z);
i++;
}
for(int i=l;i<j;i++){
add(a[i].z,-a[i].w);
}
}
int cnt[N];
int main(){
cin>>nn>>m;
for(int i=1;i<=nn;i++)cin>>b[i].x>>b[i].y>>b[i].z;
sort(b+1,b+1+nn,cmp);
int c=0;
for(int i=1;i<=nn;i++){
c++;
if(b[i].x!=b[i+1].x||b[i].y!=b[i+1].y||b[i].z!=b[i+1].z){
a[++n]=b[i],a[n].w=c,c=0;
}
}
cdq(1,n);
for(int i=1;i<=n;i++){
cnt[a[i].ans+a[i].w-1]+=a[i].w;
}
for(int i=0;i<nn;i++)cout<<cnt[i]<<endl;
return 0;
}
后记
四维偏序在最后面!
题面
前言
再来道没有那么应用的题
正文
正难则反?
你会发现正并不难
只要记录每次删除对逆序对产生的贡献即可
还是记
发现对答案产生贡献的点对满足
好的
三维数点
在 cdq 里面正着来一遍,反着来一遍即可
代码
#include <bits/stdc++.h>
using namespace std;
const int N=1e6+6;
#define int long long
int n,m,po[N],tot,ans[N],c[N];
struct node{int o,pos,val,id,t;}p[N];
bool cmp(node a,node b){return a.pos<b.pos;}
int lowbit(int x){return x&-x;}
void add(int x,int d){
for(;x<=n;x+=lowbit(x))c[x]+=d;
}
int query(int x){
int res=0;
for(;x;x-=lowbit(x))res+=c[x];
return res;
}
void cdq(int l,int r){
if(l==r)return;
int mid=(l+r)>>1;
cdq(l,mid),cdq(mid+1,r);
sort(p+l,p+mid+1,cmp),sort(p+mid+1,p+r+1,cmp);
int j=l,i=mid+1;
while(i<=r){
while(j<=mid&&p[j].pos<=p[i].pos){
add(p[j].val,p[j].o);
j++;
}
ans[p[i].id]+=p[i].o*(query(n)-query(p[i].val));
i++;
}
for(int i=l;i<j;i++)add(p[i].val,-p[i].o);
j=mid,i=r;
while(i>mid){
while(j>=l&&p[j].pos>=p[i].pos){
add(p[j].val,p[j].o);
j--;
}
ans[p[i].id]+=p[i].o*query(p[i].val-1);
i--;
}
for(int i=mid;i>j;i--)add(p[i].val,-p[i].o);
}
signed main(){
cin>>n>>m;
for(int i=1;i<=n;i++){
int x;cin>>x;
po[x]=i;
p[++tot]=node{1,i,x,0,tot};
}
for(int i=1;i<=m;i++){
int x;cin>>x;
p[++tot]=node{-1,po[x],x,i,tot};
}
cdq(1,tot);
for(int i=1;i<=m;i++)ans[i]+=ans[i-1];
for(int i=0;i<m;i++)cout<<ans[i]<<endl;
return 0;
}
后记
也很板叭(博客没保存!!!)
题面
前言
小容斥
正文
只算每个点左下角就是三维数点
加上小容斥
代码
#include <bits/stdc++.h>
using namespace std;
const int N=2e6+5;
struct node{int t,x,y,id,val;}p[N];
int w,cnt;
bool cmp(node x,node y){return x.x<y.x;}
int c[N];
int lowbit(int x){return x&-x;}
void add(int x,int y){
for(;x<=w;x+=lowbit(x))c[x]+=y;
}
int query(int x){
int res=0;
for(;x;x-=lowbit(x))res+=c[x];
return res;
}
void cdq(int l,int r){
if(l==r)return;
int mid=(l+r)>>1;
cdq(l,mid),cdq(mid+1,r);
sort(p+l,p+mid+1,cmp),sort(p+mid+1,p+r+1,cmp);
int i=mid+1,j=l;
while(i<=r){
while(j<=mid&&p[j].x<=p[i].x){
if(!p[j].id)add(p[j].y,p[j].val);
j++;
}
if(p[i].id)p[i].val+=query(p[i].y);
i++;
}
for(int i=l;i<j;i++)if(!p[i].id)add(p[i].y,-p[i].val);
}
bool cmp1(node a,node b){return a.t<b.t;}
int main(){
int o;
cin>>o>>w;
w++;
while(o!=3){
cin>>o;
if(o==1){
int x,y,a;
cin>>x>>y>>a;
p[++cnt]=node{cnt,x+1,y+1,0,a};
}else if(o==2){
int x,y,x2,y2;
cin>>x>>y>>x2>>y2;
x2++,y2++;
p[++cnt]=node{cnt,x,y,1,0};
p[++cnt]=node{cnt,x2,y2,1,0};
p[++cnt]=node{cnt,x2,y,1,0};
p[++cnt]=node{cnt,x,y2,1,0};
}
}
cdq(1,cnt);
sort(p+1,p+1+cnt,cmp1);
for(int i=1;i<=cnt;i++){
if(p[i].id){
cout<<p[i].val+p[i+1].val-p[i+2].val-p[i+3].val<<endl;
i+=3;
}
}
return 0;
}
后记
何去何往
题面
前言
cdq 优化 dp 捏
正文
这题是在导弹拦截上又加上了一维限制
定义:
dp1[i]表示以第i枚导弹为结尾的方案中的最长不上升子序列长度
dp2[i]表示以第i枚导弹为开头的方案中的最长不上升子序列长度
f1[i]表示以第i枚导弹为结尾的最长不上升子序列方案总数
f2[i]表示以第i枚导弹为开头的最长不上升子序列方案总数
那么第一问的答案即max{dp1[i]}
第二问第i枚导弹的答案即f1[i]*f2[i]/k 且dp1[i]+dp2[i]=ans-1,ans为第一问答案,k为总方案数
还是三维
不过这次要把
为什么捏
因为
懒!
我总不能对一个排列离散化叭
cdq 时,你会发现,是左边对右边产生贡献
所以要先算完左边在进行合并
void cdq(int l,int r,int o){
if(l==r)return;
int mid=(l+r)>>1;
cdq(l,mid,o);
if(o==1){
sort(a+l,a+mid+1,cmp3);
sort(a+mid+1,a+r+1,cmp3);
}
else{
sort(a+l,a+mid+1,cmp4);
sort(a+mid+1,a+r+1,cmp4);
}
merge(l,r,o);
if(o==1)sort(a+mid+1,a+r+1,cmp1);
else sort(a+mid+1,a+r+1,cmp2);
cdq(mid+1,r,o);
}
merge 就是合并,步骤就是 dp
void merge(int l,int r,int o){
int mid=(l+r)>>1;
int j=l,i=mid+1;
while(j<=mid&&i<=r){
if((o==1&&a[j].y>=a[i].y)||(o==2&&a[j].y<=a[i].y)){
add(a[j].z,a[j].dp,a[j].f);
j++;
}else{
int val=query1(a[i].z)+1;
if(a[i].dp<val){
a[i].dp=val;
a[i].f=query2(a[i].z,val-1);
}else if(a[i].dp==val){
a[i].f+=query2(a[i].z,val-1);
}
i++;
}
}
while(i<=r){
int val=query1(a[i].z)+1;
if(a[i].dp<val){
a[i].dp=val;
a[i].f=query2(a[i].z,val-1);
}else if(a[i].dp==val){
a[i].f+=query2(a[i].z,val-1);
}
i++;
}
for(int i=l;i<j;i++)del(a[i].z);
}
代码
#include <bits/stdc++.h>
using namespace std;
const int N=1e6+6;
int n;
struct node{int x,y,z,dp;double f;}a[N];
bool cmp1(node a,node b){
if(a.x==b.x){
if(a.y==b.y)return a.z<b.z;
return a.y>b.y;
}
return a.x>b.x;
}
bool cmp2(node a,node b){
if(a.x==b.x){
if(a.y==b.y)return a.z<b.z;
return a.y<b.y;
}
return a.x<b.x;
}
bool cmp3(node a,node b){
return a.y>b.y;
}
bool cmp4(node a,node b){
return a.y<b.y;
}
int lowbit(int x){return x&-x;}
int c1[N];
double c2[N];
void add(int x,int dp,double f){
for(;x<=n;x+=lowbit(x)){
if(c1[x]<dp){
c1[x]=dp;
c2[x]=f;
}else if(c1[x]==dp)c2[x]+=f;
}
}
int query1(int x){
int res=0;
for(;x;x-=lowbit(x))res=max(res,c1[x]);
return res;
}
double query2(int x,int dp){
double res=0;
for(;x;x-=lowbit(x))if(c1[x]==dp)res+=c2[x];
return res;
}
void del(int x){
for(;x<=n;x+=lowbit(x))c1[x]=c2[x]=0;
}
void merge(int l,int r,int o){
int mid=(l+r)>>1;
int j=l,i=mid+1;
while(j<=mid&&i<=r){
if((o==1&&a[j].y>=a[i].y)||(o==2&&a[j].y<=a[i].y)){
add(a[j].z,a[j].dp,a[j].f);
j++;
}else{
int val=query1(a[i].z)+1;
if(a[i].dp<val){
a[i].dp=val;
a[i].f=query2(a[i].z,val-1);
}else if(a[i].dp==val){
a[i].f+=query2(a[i].z,val-1);
}
i++;
}
}
while(i<=r){
int val=query1(a[i].z)+1;
if(a[i].dp<val){
a[i].dp=val;
a[i].f=query2(a[i].z,val-1);
}else if(a[i].dp==val){
a[i].f+=query2(a[i].z,val-1);
}
i++;
}
for(int i=l;i<j;i++)del(a[i].z);
}
void cdq(int l,int r,int o){
if(l==r)return;
int mid=(l+r)>>1;
cdq(l,mid,o);
if(o==1){
sort(a+l,a+mid+1,cmp3);
sort(a+mid+1,a+r+1,cmp3);
}
else{
sort(a+l,a+mid+1,cmp4);
sort(a+mid+1,a+r+1,cmp4);
}
merge(l,r,o);
if(o==1)sort(a+mid+1,a+r+1,cmp1);
else sort(a+mid+1,a+r+1,cmp2);
cdq(mid+1,r,o);
}
int dp1[N],dp2[N];
double f1[N],f2[N];
int main(){
cin>>n;
for(int i=1;i<=n;i++){
cin>>a[i].x>>a[i].y;
a[i].z=i,a[i].dp=1,a[i].f=1;
}
sort(a+1,a+1+n,cmp1);
cdq(1,n,1);
int ans=0;
for(int i=1;i<=n;i++){
dp1[a[i].z]=a[i].dp;
f1[a[i].z]=a[i].f;
ans=max(ans,dp1[a[i].z]);
}
cout<<ans<<endl;
for(int i=1;i<=n;i++){
a[i].z=n-a[i].z+1;
a[i].dp=a[i].f=1;
}
sort(a+1,a+n+1,cmp2);
cdq(1,n,2);
double k=0;
for(int i=1;i<=n;i++){
dp2[n-a[i].z+1]=a[i].dp;
f2[n-a[i].z+1]=a[i].f;
if(dp2[n-a[i].z+1]==ans)k+=f2[n-a[i].z+1];
}
for(int i=1;i<=n;i++){
if(dp1[i]+dp2[i]==ans+1)printf("%.5lf ",f1[i]*f2[i]/k);
else cout<<"0.00000 ";
}
return 0;
}
后记
明明心里很清楚,但还是要问……
题面
前言
四维数点来喽
不过芙宁娜世界第一可爱(万叶叶用可爱形容不太好叭)
正文
思考一下三维偏序中的 CDQ 分治做法:
本质就是把每个
四维偏序中珂以沿用三维偏序中的思路。
把若干
第二层 CDQ 和三维偏序中的 CDQ 差不多。
每个元素需要再加个变量 o 。表示的意思就是第一维的
第一层 CDQ 的时候把
第二层的改变是:插入的时候只用当 o=0 的时候才能插入,询问的时候只有当 o=1 的时候才能询问。
同样这个做法可以扩展到 k 维:用若干个变量记录前面几维的 0/1 状态。
代码
#include <bits/stdc++.h>
using namespace std;
#define int long long
const int N=1e6+6;
const int inf=0x3f3f3f3f3f3f3f3fll;
int n,b[N],tot;
struct node{int x,y,z,w,val,o,mx,id;}a[N],tmp[N];
bool cmp1(node a,node b){
if(a.x==b.x){
if(a.y==b.y){
if(a.z==b.z){
if(a.w==b.w)return a.val<b.val;
return a.w<b.w;
}
return a.z<b.z;
}
return a.y<b.y;
}
return a.x<b.x;
}
bool cmp2(node a,node b){
if(a.y==b.y){
if(a.z==b.z){
if(a.w==b.w)return a.val<b.val;
return a.w<b.w;
}
return a.z<b.z;
}
return a.y<b.y;
}
bool cmp3(node a,node b){
if(a.z==b.z){
if(a.w==b.w)return a.val<b.val;
return a.w<b.w;
}
return a.z<b.z;
}
int y[N],y2[N],c[N];
int lowbit(int x){return x&-x;}
void add(int x,int d){
for(;x<=n;x+=lowbit(x))c[x]=max(c[x],d);
}
int query(int x){
int res=-inf;
for(;x;x-=lowbit(x))res=max(res,c[x]);
return res;
}
void clear(int x){
for(;x<=n;x+=lowbit(x))c[x]=-inf;
}
void cdq2(int l,int r){
if(l==r)return;
int mid=(l+r)>>1;
cdq2(l,mid);
sort(a+l,a+mid+1,cmp3);
sort(a+mid+1,a+r+1,cmp3);
int j=l,i=mid+1;
while(i<=r){
while(j<=mid&&a[j].z<=a[i].z){
if(a[j].o)add(a[j].w,a[j].mx);
j++;
}
if(!a[i].o)a[i].mx=max(a[i].mx,query(a[i].w)+a[i].val);
i++;
}
for(int i=l;i<j;i++)if(a[i].o)clear(a[i].w);
for(int i=l;i<=r;i++)tmp[y2[a[i].id]]=a[i];
for(int i=l;i<=r;i++)a[i]=tmp[i];
cdq2(mid+1,r);
}
void cdq(int l,int r){
if(l==r)return;
int mid=(l+r)>>1;
cdq(l,mid);
for(int i=l;i<=mid;i++)a[i].o=1;
for(int i=mid+1;i<=r;i++)a[i].o=0;
sort(a+l,a+r+1,cmp2);
for(int i=l;i<=r;i++)y2[a[i].id]=i;
cdq2(l,r);
for(int i=l;i<=r;i++)tmp[y[a[i].id]]=a[i];
for(int i=l;i<=r;i++)a[i]=tmp[i];
cdq(mid+1,r);
}
signed main(){
cin>>n;
for(int i=1;i<=n;i++){
cin>>a[i].x>>a[i].y>>a[i].z>>a[i].w>>a[i].val;
b[i]=a[i].w;
}
// sort(b+1,b+1+n);
// tot=unique(b+1,b+1+n)-b-1;
// for(int i=1;i<=n;i++)a[i].w=lower_bound(b+1,b+1+tot,a[i].w)-b;
sort(a+1,a+n+1,cmp1);
int cnt=0;
for(int i=1;i<=n;i++){
if((a[i].x==a[i-1].x&&a[i].y==a[i-1].y)&&(a[i].z==a[i-1].z&&a[i].w==a[i-1].w)){
a[cnt].val+=max(0ll,a[i].val);
}else{
a[++cnt]=a[i];
}
}
for(int i=1;i<=n;i++)a[i].mx=a[i].val,a[i].id=i,y[a[i].id]=i;
//memset(c,-inf,sizeof(c));
cdq(1,n);
int ans=-inf;
for(int i=1;i<=n;i++)ans=max(ans,a[i].mx);
cout<<ans;
return 0;
}
后记
真希望从没遇见过你们
过去的我
很享受独处呢
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享4款.NET开源、免费、实用的商城系统
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 记一次.NET内存居高不下排查解决与启示
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了