初学cdq分治

初学cdq分治,推荐:__stdcall,讲的很详细很清楚,cdq分治解决离线问题特别方便,拿一个树状数组的模板题入门cdq分治,洛谷 P3374 ,点修改求区间和,先将每次操作存起来,每次查询 l r 的和,分成两次操作:查询 l-1 的前缀和和 r 的前缀和,特别标记,前面一次是减法,后面一次是加法, 并且标记这是第几次操作即这两次操作属于哪个答案的操作,然后根据操作的位置进行归并排序,每次把两个区间合并,由于右边区间的修改操作对右区间的查询操作贡献已经算完,只要统计左边区间的修改操作造成的前缀和对右区间的查询操作贡献就可以了,模拟一下归并排序就很容易懂了。

p3374代码:

#include<cstdio>
typedef long long ll;
const int maxn=5e5+10;
struct Query
{
    int type,idx,val;
    bool operator<(const Query& rhs)const
    {
        if(idx==rhs.idx)
        return type<rhs.type;
        return idx<rhs.idx;
    }
}Q[maxn*3],tmp[maxn*3];
int tot=0,cnt=0;
ll ans[maxn];
void cdq(int l,int r)
{
    if(r==l)return;
    int m=(l+r)/2;
    cdq(l,m);cdq(m+1,r);
    ll sum=0;
    int p=l,q=m+1,o=0;
    while(p<=m&&q<=r)
    if(Q[p]<Q[q])
    {
        if(Q[p].type==1)sum+=Q[p].val;
        tmp[o++]=Q[p++];
    }
    else
    {
        if(Q[q].type==2)ans[Q[q].val]-=sum;
        else if(Q[q].type==3)ans[Q[q].val]+=sum;
        tmp[o++]=Q[q++];
    }
    while(p<=m)tmp[o++]=Q[p++];
    while(q<=r)
    {
        if(Q[q].type==2)ans[Q[q].val]-=sum;
        else if(Q[q].type==3)ans[Q[q].val]+=sum;
        tmp[o++]=Q[q++];		
    }
    for(int i=0;i<o;i++)
    Q[l+i]=tmp[i];
}
int main()
{
    int n,m,x,l,r;
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&Q[++cnt].val);
        Q[cnt].type=1,Q[cnt].idx=i;
    }
    while(m--)
    {
        scanf("%d",&Q[++cnt].type);
        if(Q[cnt].type==1)
        scanf("%d%d",&Q[cnt].idx,&Q[cnt].val);
        else
        {
            scanf("%d%d",&l,&r);
            Q[cnt].idx=l-1,Q[cnt].val=++tot;
            Q[++cnt].type=3,Q[cnt].idx=r,Q[cnt].val=tot; 
        }
    }
    cdq(1,cnt);
    for(int i=1;i<=tot;i++)printf("%lld\n",ans[i]);
}

Bubble Cup 11 - Finals [Online Mirror, Div. 2]   A. AI robots

这个题可以先将每个机器人的可见范围 r 从小到大排序,这样如果左区间的机器人能看到右区间的机器人,那么右区间机器人肯定能看到左区间的,减少了考虑的因素,接下来对机器人按照q值归并排序,对于左区间每个机器人,将右区间所有q值与之相差<=k的机器人的x坐标插入树状数组,然后查询区间 x-r r+r的和即可,x和r分别代表左区间该机器人的坐标和可见范围。

#include<cstdio>
#include<algorithm>
using namespace std;
typedef long long ll;
const int maxn=1e5+10;
struct Query
{
	int x,r,q,X,L,R;
	bool operator<(const Query& rhs)const
	{
		return r<rhs.r;
	}
}Q[maxn];
int n,k,sz=0,c[maxn*3],a[maxn*3];
ll ans=0;
int low(int x)
{
	return x&(-x);
}
void update(int x,int v)
{
	while(x<=sz)
	{
		c[x]+=v;
		x+=low(x);
	}
}
ll query(int x)
{
	if(x<=0)return 0;
	ll res=0;
	while(x)
	{
		res+=c[x];
		x-=low(x);
	}
	return res;
}
bool cmp(Query t1,Query t2)
{
	return t1.q<t2.q;
}
void cdq(int l,int r)
{
	if(l==r)return;
	int m=(l+r)/2;
	cdq(l,m),cdq(m+1,r);
	int p=m+1,q=m+1;
	for(int i=l;i<=m;i++)
	{
		while(q<=r&&Q[q].q<=Q[i].q+k)
		update(Q[q].X,1),q++;
		while(p<=r&&Q[p].q<Q[i].q-k)
		update(Q[p].X,-1),p++;
		ans+=query(Q[i].R)-query(Q[i].L-1);
	}
	for(int i=p;i<q;i++)update(Q[i].X,-1);
	inplace_merge(Q+l,Q+m+1,Q+r+1,cmp);
}
int main()
{
	scanf("%d%d",&n,&k);
	for(int i=1;i<=n;i++)
	{
		scanf("%d%d%d",&Q[i].x,&Q[i].r,&Q[i].q);
		a[++sz]=Q[i].x-Q[i].r;
		a[++sz]=Q[i].x;
		a[++sz]=Q[i].x+Q[i].r;
	}
	sort(a+1,a+1+sz);
	sz=unique(a+1,a+1+sz)-a-1;
	for(int i=1;i<=n;i++)
	{
		Q[i].X=lower_bound(a+1,a+1+sz,Q[i].x)-a;
		Q[i].L=lower_bound(a+1,a+1+sz,Q[i].x-Q[i].r)-a;
		Q[i].R=lower_bound(a+1,a+1+sz,Q[i].x+Q[i].r)-a;
	}
	sort(Q+1,Q+1+n);
	cdq(1,n);
	printf("%I64d\n",ans);
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

长沙橘子猫

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值