bzoj3295[Cqoi2011] 动态逆序对

题目链接:bzoj3295

题目大意:

对于序列A,它的逆序对数定义为满足i<j,且Ai>Aj的数对(i,j)的个数。给1到n的一个排列,按照某种顺序依次删除m个元素,你的任务是在每次删除一个元素之前统计整个序列的逆序对数。


题解:

cdq分治

当前的逆序对数=总逆序对数-含删除元素的逆序对数+删除元素序列中新增加的逆序对数(因为一个逆序对会被重复删

而总逆序对数和含删除元素的逆序对数可以在一开始处理出来。所以目标就是要求每个删除操作后删除元素序列中新增加的逆序对数,这个用cdq来做。

额删除序列就是被删除的元素的序列,按原序列的顺序来排序。譬如样例删了5 1 4 2这四个元素,那么删除序列就是1 5 4 2

要用LL!!!

其实↑都是对popoqqq的理解orzorzorz

呐第一道cdq分治,所以说cdq分治是划分操作序列?每次处理出前半部分操作对后半部分操作的影响?吧做多点啊。

#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
#define maxn 110000
typedef long long LL;

struct node
{
	LL x,y,pos;
}q[maxn],tol[maxn],tor[maxn];
bool cmp(node x,node y)
{
	return x.y<y.y;
}LL a[maxn];
LL n,m,tot,ans,w[maxn],c[maxn];
LL tim[maxn],cnt[maxn],f[maxn];
//tot-当前的时间 tim[x]-c[x]的时间位置
//额这个意会(?)就是一个顺序(时间戳?而已
//cnt[i]-含有位置i这个元素的逆序对数
//ans-当前答案 f[i]-第i个删除操作后新删除序列增加的逆序对数
LL lowbit(LL x){return x&(-x);}
//向上修改
void change1(LL x){
	for (x;x<=n;x+=lowbit(x)) {
		if (tim[x]!=tot) c[x]=0,tim[x]=tot;
		c[x]++;
	}
}
//向下修改
void change2(LL x){
	for (x;x>0;x-=lowbit(x)) {
		if (tim[x]!=tot) c[x]=0,tim[x]=tot;
		c[x]++;
	}
}
//向上查询
LL query1(LL x)
{
	LL ret=0;
	for (x;x<=n;x+=lowbit(x)) if (tim[x]==tot) ret+=c[x];
	return ret;
}
//向下查询
LL query2(LL x)
{
	LL ret=0;
	for (x;x>0;x-=lowbit(x)) if (tim[x]==tot) ret+=c[x];
	return ret;
}
void solve(LL l,LL r)//cdq分治
{
	LL mid=(l+r)>>1;
	if (l==r) 
	{
		printf("%lld\n",ans);
		ans-=cnt[q[mid].y];
		ans+=f[mid];return;
	}
	//分,处理前半部分(因为后半部分对前半部分没有影响
	LL i,lnum=0,rnum=0;
	for (i=l;i<=r;i++)
	 if (q[i].pos<=mid) tol[++lnum]=q[i];
	 else tor[++rnum]=q[i];
	for (i=0;i<lnum;i++) q[l+i]=tol[i+1];
	for (i=0;i<rnum;i++) q[l+i+lnum]=tor[i+1];
	solve(l,mid);
	//处理前半部分对后半部分的影响 
	//就是搞 处理完前半部分的删除后 f[]的改变 
	tot++;LL j=l;
	for (i=mid+1;i<=r;i++)
	{
		for (;j<=mid && q[j].y<q[i].y;j++)
		  change2(q[j].x);
		f[q[i].pos]+=query1(q[i].x);
	}
	tot++;j=mid;
	for (i=r;i>mid;i--)
	{
		for (;j>=l && q[j].y>q[i].y;j--)
			change1(q[j].x);
		f[q[i].pos]+=query2(q[i].x);
	}
	solve(mid+1,r);//搞后半部分
	lnum=l;rnum=mid+1;//归并
	for (i=l;i<=r;i++)
	{
		if ( (q[lnum].y<q[rnum].y || rnum>r) && lnum<=mid)
			tol[i]=q[lnum++];
		else tol[i]=q[rnum++];
	}
	for (i=l;i<=r;i++) q[i]=tol[i];
}
int main()
{
	//freopen("a.in","r",stdin);
	//freopen("a.out","w",stdout);
	memset(tim,0,sizeof(tim));
	LL i;
	scanf("%lld%lld",&n,&m);tot=0;
	for (i=1;i<=n;i++)
	{
		scanf("%lld",&a[i]);
		w[a[i]]=i;
		cnt[i]=query1(a[i]);
		change2(a[i]);
		ans+=cnt[i];
	}
	tot++;
	for (i=n;i>=1;i--)
	{
		cnt[i]+=query2(a[i]);
		change1(a[i]);
	}
	for (i=1;i<=m;i++)
	{
		scanf("%lld",&q[i].x);
		q[i].y=w[q[i].x];
		q[i].pos=i;f[i]=0;
	}
	sort(q+1,q+1+m,cmp);
	solve(1,m);
	return 0;
}


划分序列?每次处理出前半部分对后半部分的影响
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
根据引用所述,交错序列是一个仅由0和1构成的序列,其中没有相邻的1(可以有相邻的0)。特征值定义为x^ay^b,其中x和y分别表示0和1出现的次数。长度为n的交错序列可能有多个。问题要求计算所有长度为n的交错序列特征值的和除以m的余数。 根据引用所述,输入文件包含一个行,该行包含三个整数n、a、b和m。其中,1≤n≤10000000,0≤a、b≤45,m<100000000。 为了解决这个问题,可以使用动态规划和矩阵快速幂优化的方法,具体实现可以参考引用提到的相关算法。算法的思路是通过计算长度为n的交错序列的特征值,然后将所有特征值求和并对m取余数。 具体步骤如下: 1. 使用动态规划计算长度为n的所有交错序列的特征值,将结果保存在一个矩阵中。 2. 使用矩阵快速幂优化,将动态规划的过程进行优化。 3. 对优化后的结果进行求和,并对m取余数。 4. 输出结果。 参考引用给出的博客中的代码实现,可以帮助你更好地理解和实现该算法。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* *3* [BZOJ5298 CQOI2018 交错序列 【DP+矩阵快速幂优化】*](https://blog.csdn.net/weixin_30892987/article/details/99470493)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v92^chatsearchT0_1"}}] [.reference_item style="max-width: 100%"] [ .reference_list ]

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值