【可持久化线段树】2019雅礼集训 permutation

题目:

在这里插入图片描述


分析:

比较恶心的一道题。
首先,将式子转化一下,变成
∑ A P i ∗ ( n − i + 1 ) \sum A_{P_i}*(n-i+1) APi(ni+1)
这里应该很好理解,无非就是把当前排列倒序,然后计算贡献而已。

但是只有这么写才比较容易看出下一步的性质:
显然,题目的最小值,必然是当 P i P_i Pi为升序时(否则可以通过交换一对来得到更小的答案)

然后对某个排列,转移到任意其他排列,可以通过这种方式:
将目标排列的第一个数,从当前排列中移动到第一位,再讲目标排列的第二个数,从当前排列中移动到第二位……

显然,每次这样做会使得答案不减。(每次操作后的贡献为 ∑ j i − 1 ( A i − A j ) , 又 A i > A j \sum_{j}^{i-1}(A_i-A_j),又A_i>A_j ji1(AiAj),Ai>Aj

所以,可以把这看做是转移的方式。

定义 g ( u , l e n ) g(u,len) g(u,len)表示在当前状态下,把u向前移动len的贡献。

由于 g ( u , l e n ) g(u,len) g(u,len)随len单调,所以初始状态下可以选择的转移必然为: g ( 2 , 1 ) , g ( 3 , 1 ) , g ( 4 , 1 ) … … g ( n , 1 ) g(2,1),g(3,1),g(4,1)……g(n,1) g(2,1),g(3,1),g(4,1)g(n,1)

现在考虑当使用一次转移后,能得到哪些新的转移。
假设当前使用了 g ( u , l e n ) g(u,len) g(u,len)

首先,肯定能转移到 g ( u , l e n + 1 ) g(u,len+1) g(u,len+1),其含义为:在原序列中,将u移动len+1位。
然后,还能转移到 g ( u i , l e n i ) g(u_i,len_i) g(ui,leni),其中 ( i ∈ [ u − l e n + 1 , n ] ) (i\in[u-len+1,n]) (i[ulen+1,n]),其含义为:在移动了u后,再移动其余位置的转移。

第一种转移很好操作,直接线段树上更改就好了。

问题是第二种怎么搞。

观察到,第二种转移方式,无非就只有 u − l e n u-len ulen及以前的被删掉了,然后 u u u的位置被更改了。其余部分和原线段树没有任何更改。所以就可以用可持久化线段树实现这一过程。

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
#define SF scanf
#define PF printf
#define MAXN 300010
#define INF 1000000000000000000ll
using namespace std;
typedef long long ll;
typedef pair<ll,int> PLI;
struct node{
	int u,len,s;
	ll g;	
	node *ch[2];
	void pushup(){
		s=ch[0]->s+ch[1]->s;
		if(ch[0]->g < ch[1]->g)
			u=ch[0]->u,len=ch[0]->len,g=ch[0]->g;
		else
			u=ch[0]->s+ch[1]->u,len=ch[1]->len,g=ch[1]->g;
	}
}tree[MAXN*40];
node *ncnt=tree,*NIL;
struct Tree{
	node *ori,*now;
	ll sum;
}Root[MAXN*40];
int cnt;
priority_queue<PLI,vector<PLI>,greater<PLI> >que;
ll a[MAXN];
void init(){
	NIL=ncnt=tree;
	NIL->ch[0]=NIL->ch[1]=NIL;
	NIL->u=NIL->len=NIL->s=0;
	NIL->g=INF;
}
node *Newnode(){
	++ncnt;
	ncnt->ch[0]=ncnt->ch[1]=NIL;
	ncnt->u=ncnt->len=ncnt->s=0;
	ncnt->g=INF;
	return ncnt;	
}
void build(int l,int r,node *&x){
	x=Newnode();
	if(l==r){
		x->u=x->s=x->len=1;	
		if(l>1) x->g=a[l]-a[l-1];
		else x->g=INF;
		return ;
	}
	int mid=(l+r)>>1;
	build(l,mid,x->ch[0]);
	build(mid+1,r,x->ch[1]);
	x->pushup();
}
ll query(node *x,int l,int r,int pos){
	if(l==r)
		return a[l];
	int mid=(l+r)>>1;
	if(pos<=x->ch[0]->s)
		return query(x->ch[0],l,mid,pos);
	else
		return query(x->ch[1],mid+1,r,pos - x->ch[0]->s);
}
bool flag;
void ChangePoint(node *&x,int l,int r,int pos,ll newg,int news){
	node *y=Newnode();
	*y=*x;
	x=y;
	if(l==r){
		if(flag==1)
			x->g+=newg;
		else
			x->g=newg,x->s=news;
		return ;
	}
	int mid=(l+r)>>1;
	if(pos<=x->ch[0]->s)
		ChangePoint(x->ch[0],l,mid,pos,newg,news);
	else
		ChangePoint(x->ch[1],mid+1,r,pos - x->ch[0]->s,newg,news);
	x->pushup();
}
void Delete(node *&x,int l,int r,int len){
	if(len==0)
		return ;
	int mid=(l+r)>>1;
	node *y=++ncnt;
	*y=*x;
	x=y;
	if(x->ch[0]->s > len)
		Delete(x->ch[0],l,mid,len);
	else
		Delete(x->ch[1],mid+1,r,len - x->ch[0]->s),x->ch[0]=NIL;
	x->pushup();
}
int n,k;
void Extend(int px){
	int newid=++cnt;
	int u=Root[px].now->u,len=Root[px].now->len;
	int l=u-len;
//	PF("[%d %d]",u,len);
	Root[newid].sum=Root[px].sum+Root[px].now->g;
	Root[newid].ori=Root[px].ori;
	ChangePoint(Root[newid].ori,1,n,u,INF,0);
	Delete(Root[newid].ori,1,n,l-1);
	if(len+1<=Root[newid].ori->s){
		ll newval=query(Root[newid].ori,1,n,len+1)-query(Root[newid].ori,1,n,len);
		ChangePoint(Root[newid].ori,1,n,len+1,newval,1);	
	}
	ChangePoint(Root[newid].ori,1,n,1,INF,1);
	Root[newid].now=Root[newid].ori;
//	PF("{%lld+%lld %d}\n",Root[newid].now->g,Root[newid].sum,newid);
	que.push(make_pair(Root[newid].now->g+Root[newid].sum,newid));
	
	if(l>1){
		ll newval=query(Root[px].now,1,n,u)-query(Root[px].now,1,n,l-1);
		flag=1;
		ChangePoint(Root[px].now,1,n,u,newval,1);	
		flag=0;
	}
	else
		ChangePoint(Root[px].now,1,n,u,INF,1);
	que.push(make_pair(Root[px].now->g+Root[px].sum,px));
//	PF("{%lld+%lld %d}\n",Root[px].now->g,Root[px].sum,px);
}
int main(){
	init();
	SF("%d%d",&n,&k);
	for(int i=1;i<=n;i++)
		SF("%lld",&a[i]);
	sort(a+1,a+1+n);
	build(1,n,Root[0].ori);
	for(int i=1;i<=n;i++)
		Root[0].sum+=a[i]*(n-i+1);
	PF("%lld\n",Root[0].sum);
	Root[0].now=Root[0].ori;
	que.push(make_pair(Root[0].now->g+Root[0].sum,0));
	while(--k){
		PLI tp=que.top();
		que.pop();
		PF("%lld\n",tp.first);
		Extend(tp.second);
	}
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值