CDQ分治
##1、与普通分治的区别
普通分治中,每一个子问题只解决它本身(可以说是封闭的)
分治中,对于划分出来的两个子问题,前一个子问题用来解决后一个子问题而不是它本身
##2、试用的情况
在很多问题中(比如大多数数据结构中),经常需要添加一些动态问题,然而对动态问题的处理总是不如静态问题来得方便,于是就有了分治
但使用分治的前提是必须有一下两个性质:
- 修改操作对区间询问的贡献独立,修改操作互相不影响
- 题目允许使用离线算法
2.1 一般步骤
- 将整个操作序列分为两个长度相等的部分(分)
- 递归处理前一部分的子问题(治1)
- 计算前一部分的子问题中的修改操作对后一部分子问题的影响(治2)
- 递归处理后一部分的子问题
特别说明:
在整个过程中,最核心的就是步骤3
此时前一部分子问题中的修改操作相对后一部分子问题来说是静态处理,因此可以更加方便地计算后一部分子问题
3.题集
3.1 51nod 1376 最长递增子序列的数量
用f[i]表示以第i个数结尾的LIS的长度和该长度的数量 len count
显然
$ f[i].first=max{f[i].first}+1,j<i&&a[j]<a[i] $
f [ i ] . s e c o n d = ∑ j f [ i ] . s e c o n d , f [ j ] . f i r s t = f [ i ] . f i r s t − 1 f[i].second= \sum_j f[i].second , f[j].first=f[i].first-1 f[i].second=∑jf[i].second,f[j].first=f[i].first−1
二维偏序,一维下标,二维值;
直接cdq分治处理:
#include<bits/stdc++.h>
using namespace std;
const int MAXN=1e5+10;
const int INF=0x3f3f3f3f;
const int MOD=1e9+7;
int n,a[MAXN];
#define P pair<int,int>
P f[MAXN];//LIS length,count
void getMax(P& x,P y)
{
if(x.first<y.first)x=y;
else if(x.first==y.first)
{
if((x.second+=y.second)>=MOD)
x.second-=MOD;
}
}
int id[MAXN];
/*排序小技巧
避开相等,间隔排序,把可能成为询问的排到第一个
*/
bool cmp(int x,int y)
{
if(a[x]!=a[y])return a[x]<a[y];
return x>y;
}
void cdq(int l,int r)
{
if(l==r)return;
int m=(l+r)>>1;
cdq(l,m);
for(int i=l;i<=r;i++)id[i]=i;
sort(id+l,id+r+1,cmp);
P maxf(0,0);
for(int i=l;i<=r;i++)
{
int idx=id[i];
if(idx<=m)getMax(maxf,f[idx]);
else
{
P cur=maxf;
++cur.first;
getMax(f[idx],cur);
}
}
cdq(m+1,r);
}
int main()
{
ios_base::sync_with_stdio(0);
scanf("%d",&n);
for(int i=1;i<=n;i++)
scanf("%d",a+i);
for(int i=1;i<=n;i++)f[i]=P(1,1);
cdq(1,n);
P ans(0,0);
for(int i=1;i<=n;i++)getMax(ans,f[i]);
printf("%d\n",ans.second);
return 0;
}
3.2 BZOJ 3262 陌上花开
3.3 HDU4742 Pinball Game 3D
【CDQ分治+树状数组】HDU 5618 Jam’s problem again
http://acm.hdu.edu.cn/showproblem.php?pid=5618
题意:给n个点,求每一个点的满足xyz都小于等于它的其他点的个数。
思路:经典的cdq分治+树状数组。 方法就是先按照x从小到大排序, 这样,x一定满足后面的大于等于前面的, 这样我们就可以不用管x了, 然后对y进行cdq分治,使得y满足关系, 在此基础上用树状数组顺便维护z坐标。 但是由于cdq分治每次总是要解决左边对右边的影响, 所以在相等时, 每次只更新了右边的点。 因此预处理一下相等的情况。
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
#include<string>
#include<vector>
#include<stack>
#include<bitset>
#include<cstdlib>
#include<cmath>
#include<set>
#include<list>
#include<deque>
#include<map>
#include<queue>
#define Max(a,b) ((a)>(b)?(a):(b))
#define Min(a,b) ((a)<(b)?(a):(b))
using namespace std;
typedef long long ll;
const double PI = acos(-1.0);
const double eps = 1e-6;
const int mod = 1000000000 + 7;
const int INF = 1000000000;
const int maxn = 100000 + 10;
int T,n,m,bit[maxn],maxd,res[maxn];
struct node {
int x, y, z, id;
node(int x=0, int y=0, int z=0, int id=0):x(x),y(y),z(z),id(id) {}
}a[maxn],b[maxn];
int sum(int x) {
int ans = 0;
while(x > 0) {
ans += bit[x];
x -= x & -x;
}
return ans;
}
void add(int x, int d) {
while(x <= maxd) {
bit[x] += d;
x += x & -x;
}
}
bool cmp1(node a, node b) {
if(a.x != b.x) return a.x < b.x;
else if(a.y != b.y) return a.y < b.y;
else return a.z < b.z;
}
bool cmp2(node a, node b) {
if(a.y != b.y) return a.y < b.y;
else return a.id < b.id;
}
void cdq(int l, int r) {
if(l == r) return ;
int m = (l + r) >> 1;
int cc = 0;
for(int i=l;i<=m;i++) b[cc++] = node(0, a[i].y, a[i].z, 0);
for(int i=m+1;i<=r;i++) b[cc++] = node(0, a[i].y,a[i].z,a[i].id);
sort(b, b+cc, cmp2);
for(int i=0;i<cc;i++) {
if(b[i].id == 0) {
add(b[i].z, 1);
}
else {
res[b[i].id] += sum(b[i].z);
}
}
for(int i=0;i<cc;i++) {
if(b[i].id == 0) add(b[i].z, -1);
}
cdq(l, m);
cdq(m+1, r);
}
int main() {
scanf("%d",&T);
while(T--) {
scanf("%d",&n);
maxd = 0;
for(int i=1;i<=n;i++) {
scanf("%d%d%d",&a[i].x,&a[i].y,&a[i].z);
a[i].id = i;
maxd = max(maxd, a[i].z);
}
sort(a+1, a+n+1, cmp1);
memset(res, 0, sizeof(int)*(n+5));
int cnt = 0;
for(int i=n;i>=1;i--) {
if(a[i].x == a[i+1].x && a[i].y == a[i+1].y && a[i].z == a[i+1].z) cnt++;
else cnt = 0;
res[a[i].id] += cnt;
}
cdq(1, n);
for(int i=1;i<=n;i++) {
printf("%d\n",res[i]);
}
}
return 0;
}