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 
#include 
#include 
#include 
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");
	}
} 

你可能感兴趣的:(Acm,主席树,启发式合并,鸿哥牛逼)