数据结构-莫队二次离线

Posted Zhangrx-

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了数据结构-莫队二次离线相关的知识,希望对你有一定的参考价值。

莫队二次离线

问题:给一个序列a,每次询问区间里面有几个逆序对

刚刚又睡了半小时,终于睡醒了

\\(n,m\\leq 1e5\\)

实现

询问

首先想一想莫队:

对于初始询问区间[l,r],将右指针r扩展到r+1,对于答案的贡献就是[l,r]里面大于a[r+1]的数量,写成数学公式就是\\(\\sum_i=l^r(a[i]>a[r+1])\\)

然后可以直接树状数组解决,时间复杂度就是\\(O(n\\sqrt nlog n)\\),一下子就飞起来了

想一下差分,贡献变成:[1,r]里面有几个大于a[r+1]减去[1,l-1]里面大于a[r+1]的数量,同样的写成数学公式:\\(\\sum_i=1^r(a[i]>a[r+1])-\\sum_i=1^l-1(a[i]>a[r+1])\\)

对于整体情况来说就是\\((\\sum_i=r+1^r^`([1,i-1],[i,i]))-([1,l-1],[r+1,r^、])\\)

其中([],[])表示两个区间内的逆序对数量(保证前面那个区间在后面那个区间前面)

对于被减数,只有n个情况,直接树状数组\\(O(nlogn)\\)预处理掉,然后前缀和计算

对于减数,将r+1记在r和l-1上,从左往右去扫描t端点,同时维护,然后处理每次到t上面的询问,相当于n次插入和\\(n\\times \\sqrt n\\)次询问。可以在插入时对于值域分块,维护块内和块与块间的前缀和

对于[l,r]端点向着其他方向乱动也是一样的,所以不再讨论

这里的二次离线体现在的地方就是减数的求解,将问题区间差分后,再次将未知端点绑在已确定位置上,然后在遍历的时候顺手更新

代码

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const ll M=1e5+10;
ll n,m,a[M],lsh[M],cnt,pos[M],lim,lazy[M],sum[M<<2];
ll sum1[M],sum2[M],f1[M],f2[M],now_ans,ans[M];//1是前缀,2是后缀 
struct Q
	ll l,r,id;
q[M];
vector<Q> L[M],R[M];
bool cmp(Q x,Q y)
	if(pos[x.l]==pos[y.l])return x.r<y.r;
	else return pos[x.l]<pos[y.l];

ll lowbit(ll x)
	return x&(-x);

void add(ll x,ll val)
	for(;x<=cnt;x+=lowbit(x))sum[x]+=val;

ll ask(ll x)
	ll res=0;
	for(;x;x-=lowbit(x))res+=sum[x];
	return res;

void init()//处理形如前缀和的逆序对,减数
	memset(sum,0,sizeof sum);
	memset(lazy,0,sizeof lazy);
	for(ll i=0;i<n;i++)
		for(auto now:L[i])
			for(ll j=now.l;j<=now.r;j++)
				f1[now.id]+=sum[a[j]]+lazy[pos[a[j]]];
			
		
		for(ll j=a[i+1]-1;j>=lim*(pos[a[i+1]]-1)+1;j--)sum[j]++;
		for(ll j=1;j<=pos[a[i+1]]-1;j++)lazy[j]++;//处理整块前缀和 
	
	memset(sum,0,sizeof sum);
	memset(lazy,0,sizeof lazy);
	for(ll i=n+1;i>=2;i--)
		for(auto now:R[i])
			for(ll j=now.l;j<=now.r;j++)
				f2[now.id]+=sum[a[j]]+lazy[pos[a[j]]];
			
		
		for(ll j=a[i-1]+1;j<=lim*pos[a[i-1]];j++)sum[j]++;
		for(ll j=pos[a[i-1]]+1;j<=pos[cnt];j++)lazy[j]++;
	

void remove(ll now)//莫队 
	if(q[now].r>q[now-1].r)now_ans+=sum1[q[now].r]-sum1[q[now-1].r]-f1[now];
	if(q[now].r<q[now-1].r)now_ans-=sum1[q[now-1].r]-sum1[q[now].r]-f1[now];
	if(q[now].l>q[now-1].l)now_ans-=sum2[q[now-1].l]-sum2[q[now].l]-f2[now];
	if(q[now].l<q[now-1].l)now_ans+=sum2[q[now].l]-sum2[q[now-1].l]-f2[now];

int main()
	scanf("%lld%lld",&n,&m);
	lim=sqrt(n);
	for(ll i=1;i<=n;i++)pos[i]=(i-1)/lim+1;
	for(ll i=1;i<=n;i++)
		scanf("%lld",&a[i]);
		lsh[i]=a[i];
	
	sort(lsh+1,lsh+1+n);
	cnt=unique(lsh+1,lsh+1+n)-lsh-1;
	for(ll i=1;i<=n;i++)a[i]=lower_bound(lsh+1,lsh+1+cnt,a[i])-lsh;
	//预处理被减数 
	for(ll i=1;i<=n;i++)
		sum1[i]=sum1[i-1]+ask(cnt-a[i]);
		add(cnt-a[i]+1,1);
	
	memset(sum,0,sizeof sum);
	for(ll i=n;i>=1;i--)
		sum2[i]=sum2[i+1]+ask(a[i]-1);
		add(a[i],1);
	
	for(ll i=1;i<=m;i++)
		scanf("%lld%lld",&q[i].l,&q[i].r);
		q[i].id=i;
	
	sort(q+1,q+1+m,cmp);
	q[0].l=1,q[0].r=0;
	for(ll i=1;i<=m;i++)//将差分后的询问挂在节点上 
		if(q[i].r>q[i-1].r)L[q[i-1].l-1].push_back((Q)q[i-1].r+1,q[i].r,i);
		if(q[i].r<q[i-1].r)L[q[i-1].l-1].push_back((Q)q[i].r+1,q[i-1].r,i);
		if(q[i].l>q[i-1].l)R[q[i].r+1].push_back((Q)q[i-1].l,q[i].l-1,i);
		if(q[i].l<q[i-1].l)R[q[i].r+1].push_back((Q)q[i].l,q[i-1].l-1,i);
	
	init();
	for(ll i=1;i<=m;i++)
		remove(i);
		ans[q[i].id]=now_ans;
	
	for(ll i=1;i<=m;i++)printf("%lld\\n",ans[i]);
	return 0;

Tigase:有时我会收到两次离线消息

【中文标题】Tigase:有时我会收到两次离线消息【英文标题】:Tigase: sometimes i receive offline messages twice 【发布时间】:2013-01-03 11:14:14 【问题描述】:

我使用 Tigase 作为 XMPP 服务器。一切正常,但有时我会收到两次离线消息,第二次是在第一次后 25 分钟后(有时是一天后),例如:

2013-01-03 11:53:00.923 xmppStreamDidReceiveMessage: 
<message xmlns="jabber:client" id="20" type="chat" 
   to="300-92@chat.quickblox.com" from="298-92@chat.quickblox.com">
   <body>r1</body><delay xmlns="urn:xmpp:delay" stamp="2013-01-03T09:52:55.392+0000" 
   from="chat.quickblox.com">Offline Storage - ip-10-40-233-192.ec2.internal</delay>
   </message>


2013-01-03 11:53:25.091 QBChat/xmppStreamDidReceiveMessage: 
<message xmlns="jabber:client" id="20" type="chat" 
    to="300-92@chat.quickblox.com" from="298-92@chat.quickblox.com">
    <body>r1</body><delay xmlns="urn:xmpp:delay" stamp="2013-01-03T09:52:55.418+0000" 
    from="chat.quickblox.com">Offline Storage - ip-10-40-233-192.ec2.internal</delay>
    </message>

它们具有相同的 ID 和文本。

我不知道问题是什么,没有任何复制,随机复制。 第一个用户只是向第二个发送消息,第二个登录/注销,登录/注销..

谢谢你的建议

更新: 有时我会收到 2 条相同的消息:第 1 条是普通消息,第 2 条带有“延迟”参数

【问题讨论】:

【参考方案1】:

戳记不同,这表明该消息在数据库中记录了两次。您是否有机会使用网络客户端? Web客户端可能会认为服务器没有收到消息并再次发送它。但是,那么标记差异可能是几秒钟而不是几毫秒。

在我看来,原始发件人发送了两次消息,并在离线存储中记录了 2 次,然后传递给接收者。但是,如果发件人有时会发送两次消息,那么也会发生正常的非离线消息有时会加倍......

用这么少的信息真的很难说清楚。

【讨论】:

添加更新 - 你有什么想法吗?

以上是关于数据结构-莫队二次离线的主要内容,如果未能解决你的问题,请参考以下文章

第十四分块(前体)(二次离线莫队)

Tigase:有时我会收到两次离线消息

SD6.22集训总结

二维莫队(离线)

莫队算法(离线)

莫队算法(离线区间查询问题)(莫队,分块)