CDQ分治

CDQ分治

一般珂以替代一些较复杂的高级数据结构,能用来处理偏序问题、优化dp转移等。
大概思路就是分治处理点对的关系,分成三类点:1i<jmid1imid<jnmid<i<jn,然后用额外的 O(logn) 的时间去计算第二类点,一般是用树状数组或者线段树维护。

P3810 【模板】三维偏序(陌上花开)

先按 a 为第一关键字排序,去重,然后分治。分治中我们要解决的是 b,c 的二维偏序,考虑使用树状数组求解,对两边序列分别按 b 为第一关键字排序,用双指针的维护方法不断在树状数组中统计答案,因为只会走一遍所以复杂度 O(nlogn)。最后统计答案的时候注意每个 i 实际答案是 ansi+cnti1

code

点击查看代码
#include<bits/stdc++.h>
using namespace std;
#define ll long long
const ll N=2*114514,M=1919810;
struct xx{
	ll a,b,c,cnt,ans;
	bool operator !=(const xx &lxl)const{
		return a!=lxl.a||b!=lxl.b||c!=lxl.c;
	}
}a[N];
ll n,m,s[N],res[N];
ll lowbit(ll x){return x&-x;}
void add(ll x,ll k){
	while(x<=m){
		s[x]+=k;
		x+=lowbit(x);
	}
}
ll query(ll x){
	ll ans=0;
	while(x){
		ans+=s[x];
		x-=lowbit(x);
	}
	return ans;
}
bool cmp1(xx x,xx y){
	return x.a==y.a?(x.b==y.b?x.c<y.c:x.b<y.b):x.a<y.a;
}
bool cmp2(xx x,xx y){
	return x.b==y.b?x.c<y.c:x.b<y.b;
}
void calc(ll l,ll r){
	if(l==r) return;
	ll mid=l+r>>1;
	calc(l,mid),calc(mid+1,r);
	sort(a+l,a+mid+1,cmp2),sort(a+mid+1,a+r+1,cmp2);
	ll j=l;
	for(int i=mid+1;i<=r;++i){
		while(a[i].b>=a[j].b&&j<=mid){
			add(a[j].c,a[j].cnt);
			++j;
		}
		a[i].ans+=query(a[i].c);
	}
	for(int i=l;i<j;++i) add(a[i].c,-a[i].cnt);
}
int main(){
	ios::sync_with_stdio(0);
	cin.tie(0); cout.tie(0);
	cin>>n>>m;
	for(int i=1;i<=n;++i) cin>>a[i].a>>a[i].b>>a[i].c;
	sort(a+1,a+n+1,cmp1);
	ll tot=0,top=0;
	for(int i=1;i<=n;++i){
		++top;
		if(a[i]!=a[i+1]) a[++tot]=a[i],a[tot].cnt=top,top=0;
	}
	calc(1,tot);
	for(int i=1;i<=tot;++i) res[a[i].ans+a[i].cnt-1]+=a[i].cnt;
	for(int i=0;i<n;++i) cout<<res[i]<<'\n';
	return 0;
} 

P2487 [SDOI2011] 拦截导弹

先考虑朴素算法:第一问就直接 O(n2) dp转移,第二问的话我们珂以多开几个数组:f2i 表示以 i 为开头的最长不上升子序列的长度,再开两个数组 g1,g2 记录以 i 作为结尾和开头的最长不上升子序列的个数。这样子每个满足 f1i+f2i1=ansi 的答案就是 g1i×g2isumsum 为总方案数。求以 i 开头的答案珂以直接把序列倒过来权值赋为相反数然后正常dp。

考虑怎么优化,观察到这个最长不下降子序列就是个三维偏序问题,但是我们此时注意计算顺序是先计算左区间再算中间段再算右区间,否则dp的转移会有问题。这里我们分治时维护的要求变成了单点修改区间查询,但是我已经不会树状数组了,就用了线段树。还有这个题好像珂以直接 O(n) 清空整个线段树就免得还要另开数组记录?另外要开long double

code

点击查看代码
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define lb long double
#define ls now<<1
#define rs now<<1|1
const ll N=2*114514,M=1919810;
ll n,m,v[N];
struct xx{
	ll t;
	lb h,v;
}a[N],b[N],c[N];
struct gx{
	lb val,cnt;
}f1[N],f2[N];
bool cmp1(xx x,xx y){
	return x.t<y.t;
}
bool cmp2(xx x,xx y){
	return x.h==y.h?x.v>y.v:x.h>y.h;
}
gx add(gx x,gx y){
	return x.val==y.val?(gx){x.val,x.cnt+y.cnt}:(x.val>y.val?x:y);
}
struct tree{
	ll l,r;
	gx v;
}t[4*N];
void pushup(ll now){
	t[now].v=add(t[ls].v,t[rs].v);
}
void build(ll now,ll l,ll r){
	t[now].l=l,t[now].r=r;
	t[now].v=(gx){0,0};
	if(l==r) return;
	ll mid=l+r>>1;
	build(ls,l,mid);
	build(rs,mid+1,r);
}
void update(ll now,ll tar,gx k){
	if(t[now].l==t[now].r){
		t[now].v=add(t[now].v,k);
		return;
	}
	ll mid=t[now].l+t[now].r>>1;
	if(tar<=mid) update(ls,tar,k);
	else update(rs,tar,k);
	pushup(now);
}
gx query(ll now,ll x,ll y){
	if(t[now].l>=x&&t[now].r<=y) return t[now].v;
	ll mid=t[now].l+t[now].r>>1;
	gx ans=(gx){0,0};
	if(x<=mid) ans=add(ans,query(ls,x,y));
	if(y>mid) ans=add(ans,query(rs,x,y));
	return ans;
}
void clean(ll now,ll tar){
	t[now].v=(gx){0,0};
	if(t[now].l==t[now].r) return;
	ll mid=t[now].l+t[now].r>>1;
	if(tar<=mid) clean(ls,tar);
	else clean(rs,tar);
}
void calc(ll l,ll r,gx *dp){
	if(l==r){
		if(!dp[(ll)b[l].t].val)
			dp[(ll)b[l].t]=(gx){1,1};
		return;
	}
	ll mid=l+r>>1;
	calc(l,mid,dp);
	for(int i=mid+1;i<=r;++i) c[i]=b[i];
	sort(c+mid+1,c+r+1,cmp2);
	ll j=l;
	for(int i=mid+1;i<=r;++i){
		while(b[j].h>=c[i].h&&j<=mid){
			update(1,b[j].v,dp[(ll)b[j].t]);
			++j;
		}
		gx ans=query(1,c[i].v,m);
		if(ans.val){
			++ans.val;
			dp[(ll)c[i].t]=add(dp[(ll)c[i].t],ans);
		}
	}
	for(int i=l;i<=j;++i) clean(1,b[i].v);
	calc(mid+1,r,dp);
	sort(b+l,b+r+1,cmp2);
}
void solve(bool flag){
	for(int i=1;i<=n;++i) b[i]=a[i],v[i]=a[i].v;
	sort(b+1,b+n+1,cmp1);
	sort(v+1,v+n+1);
	m=unique(v+1,v+n+1)-v-1;
	for(int i=1;i<=n;++i) b[i].v=lower_bound(v+1,v+m+1,b[i].v)-v;
	build(1,1,m);
	if(!flag) calc(1,n,f1);
	else calc(1,n,f2);
}
lb ans,sum;
int main(){
	//ios::sync_with_stdio(0);
	//cin.tie(0); cout.tie(0);
	cin>>n;
	for(int i=1;i<=n;++i) cin>>a[i].h>>a[i].v,a[i].t=i;
	solve(0);
	for(int i=1;i<=n;++i) a[i]=(xx){n-i+1,-a[i].h,-a[i].v};
	solve(1);
	for(int i=1;i<=n;++i) ans=max(ans,f1[i].val);
	for(int i=1;i<=n;++i) if(ans==f1[i].val) sum+=f1[i].cnt;
	printf("%Ld\n",(ll)ans);
	for(int i=1;i<=n;++i)
		if(f1[i].val+f2[n-i+1].val-1==ans) printf("%0.5Lf ",(f1[i].cnt*1.0*f2[n-i+1].cnt*1.0/sum));
		else printf("0.00000 ");
	return 0;
}
posted @   和蜀玩  阅读(19)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
点击右上角即可分享
微信分享提示