2018青岛网络赛G题

2018青岛网络赛G题

传送门:2018青岛网络赛G题
题目大意:给n个数,然后依次删除这n个数,每删一个数等于将一个连续区间分成两个连续的区间(分开了),然后输出每次操作完所有区间的逆序数中最大的一个(一开始就是一个1到n的区间)。
题解:这题用到一个启发式合并的思想,对于集合的启发式合并就是,对于两个集合合并,我们只将小的合并到大的,我们就可以让时间复杂度大大减小,如果将一个元素放进一个集合是 O ( l o g n ) O(log n) O(logn)的,那么我们总的时间复杂度是 O ( n l o g 2 n ) O(nlog^2n) O(nlog2n)。具体证明请百度(我也说不清楚→_→)
       接下来就是暴力的时间。对于每次删除一个区间分成了两个,例如 11111111 11111111 11111111删一个数,变成 11101111 11101111 11101111,这时区间变成 111 111 111 1111 1111 1111,这时前面区间比后面区间要小,所以我们暴力求出前面区间的逆序数 O ( L l o g L ) O(LlogL) O(LlogL),然后我们就可以用总的逆序数减去短区间的逆序数再减去区间之间两两贡献的逆序数,就可以求出长的区间的逆序数。然后用一个优先队列维护逆序数最大的区间。`
       这题需要很多的细节(骚操作)。比如说我们如何快速找出删除的数字在哪个区间里面(我用染色的思想),只后用二分找出区间的左右端点。
P . s : P.s: P.s:我这个在zoj跑了2200ms,算快的)

#include <iostream>
#include <queue>
#include <algorithm>
#include <cstring>
using namespace std;
const int maxn=1e5+5;
int cnt=0,n,L,R;
int a[maxn],color[maxn],colorN;
int root[maxn],rs[maxn*30],ls[maxn*30];
long long tree[maxn*30],nxs[maxn];
struct node{
	int id;
	long long num;
	bool operator<(const node&a)const{
		return num<a.num;
	}
}u;
priority_queue<node>Q;
void update(int &rt,int prt,int L,int R,int k)
{
	if (L>k||R<k) return;
	int mid=(L+R)>>1;
	rt=++cnt;
	ls[rt]=ls[prt];rs[rt]=rs[prt];tree[rt]=tree[prt];
	if (L==R)
	{
		tree[rt]++;
		return;
	}
	update(ls[rt],ls[prt],L,mid,k);
	update(rs[rt],rs[prt],mid+1,R,k);
	tree[rt]=tree[rs[rt]]+tree[ls[rt]];
}
long long query(int rt,int prt,int L,int R,int l,int r)
{
	if (tree[rt]==tree[prt]) return 0;
	if (l>r) return 0;
	int mid=(L+R)>>1;
	if (l>R||r<L) return 0;
	if (l<=L&&r>=R) return tree[rt]-tree[prt];
	return query(ls[rt],ls[prt],L,mid,l,r)+query(rs[rt],rs[prt],mid+1,R,l,r);
}
int c[maxn];
inline int lowbit(int x){return x&-x;}
inline void add(int p,int v){for(;p<=n;p+=lowbit(p)) c[p]+=v;}
inline int sum(int p){
    int re=0;
    for(;p;p-=lowbit(p)) re+=c[p];
    return re;
}
long long Getnxs(int l,int r)
{
	long long w=0;
	for (int i=l;i<=r;i++)
	{
		w=w+sum(n)-sum(a[i]);
		add(a[i],1);
	}
	for (int i=l;i<=r;i++) add(a[i],-1);
	return w;
}	
int getl(int C,int loc)
{
	int l=1,r=loc,mid;
	while (l<=r)
	{
		mid=(l+r)>>1;
		if (color[mid]!=C)
			l=mid+1;
		else r=mid-1;
	}
	return l;
}
int getr(int C,int loc)
{
	int l=loc,r=n,mid;
	while (l<=r)
	{
		mid=(l+r)>>1;
		if (color[mid]!=C)
			r=mid-1;
		else l=mid+1;
	}
	return r;
}
int main()
{
	int T,index,temp,l,r;
	scanf("%d",&T);
	while (T--)
	{
		scanf("%d",&n);
		cnt=0;
		for (int i=1;i<=n;i++)
		{
			scanf("%d",&a[i]);
			color[i]=0;
			update(root[i],root[i-1],1,n,a[i]);
		}
		color[0]=-1;color[n+1]=-1;
		u={0,Getnxs(1,n)};
		nxs[0]=u.num;
		Q.push(u);
		printf("%lld",u.num);
		for (int i=1;i<n;i++)
		{
			scanf("%d",&index);
			index=index^u.num;			
			temp=color[index];
			l=getl(temp,index);r=getr(temp,index);
			if (r-index<index-l)
				L=index+1,R=r;
			else 
				L=l,R=index-1;
			for (int j=L;j<=R;j++)
			{
				color[j]=i;
				if (j>index) nxs[temp]-=query(root[index-1],root[l-1],1,n,a[j]+1,n);
				else nxs[temp]-=query(root[r],root[index],1,n,1,a[j]-1);
			}
			nxs[i]=Getnxs(L,R);
			nxs[temp]=nxs[temp]-query(root[index-1],root[l-1],1,n,a[index]+1,n);
			nxs[temp]=nxs[temp]-query(root[r],root[index],1,n,1,a[index]-1)-nxs[i];
			Q.push({i,nxs[i]});
			Q.push({temp,nxs[temp]});
			color[index]=-1;
			u=Q.top();
			while (nxs[u.id]!=u.num)
			{
				Q.pop();
				u=Q.top();
			}
			printf(" %lld",u.num);
		}
		printf("\n");
	}
} 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值