雅礼集训 wc2019 Day1 T2 permutation

版权声明:本文为博主原创文章,未经博主允许必须转载。 https://blog.csdn.net/qq_35950004/article/details/85958458

给出 n 个数 A i A_i
定义排列一个 1~n 的排列 P 的价值为:
i = 1 n A i P i \sum_{i=1}^nA_iP_i
请你给出排列价值前 k 小的 k 个排列的价值。
n < = 1 0 5 , m < = 1 0 5 n<=10^5,m<=10^5

友情提示,博主已被此题搅得神志不清,下文内容结构请选择和代码相近,和真理相通的部分自行判断并使用。

一个套路:如果我们每次拓展都有n种选择,那么可以用一个优先队列取出答案最小的候选方案,然后用它拓展生成新的方案,如果拓展出的方案能不重不漏,那么第k个就是第k小方案,拓展新的方案时,可以用:1.拓展最优的下一步方案。2.换当前这步的方案为次优方案。这样可以证明第k个就是第k小方案,但是我们只要快速求出最优和次优和次次优。。。。。就可以在低于nk的时间复杂度内求出k小答案。

这个题就第i次拓展是将 A i A_i ,依次和前面的换,换v次,可以发现换v次优于换v+1次,那么就可以快速求出次优。。。。。。再用一个线段树维护最优的下一个i(你可以暂时理解为下一个i大于当前i,upd:但是当你调程序到醉生欲死的时候你会发现这个并不完全正确,因为程序实现的原因,你不能就这么按从左到右的顺序拓展,偶尔也需要往回看,更深一层的理解的就是将你的第i次拓展看做第i个阶段,然后你有 n 2 n^2 个方案【对于每个 A i A_i O ( n ) O(n) 个v】,发现 A i A_i 相等的情况下v的比v+1优,那么就是一个线段树维护k路合并取最小值的第k小答案,这不就是那个普及-的丑数嘛,只不过是合并过程需要用支持区间删除的可持久化线段树维护罢了)。

然后就是为了不爆内存,用一个可支持区间删除,区间求最值的可持久化数据结构。

出题人用可持久化线段树(+一些奇怪的技巧),我。。。。。。
我其实想用可持久化treap的,这个线段树没有技巧是很毒瘤的。。。。。

WA Code:

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cctype>
#include<queue>
#define maxn 100005
#define maxpt maxn*200
#define inf 0x3f3f3f3f3f3f3f3fll
#define LL long long
using namespace std;

int n,k,a[maxn];
int lc[maxpt],rc[maxpt],sz[maxpt],mu[maxpt],mv[maxpt],tot;
LL mn[maxpt];
	
inline void upd(int now)
{	
	int l=lc[now],r=rc[now];
	mn[now] = inf;
	if(l && mn[now] > mn[l]) mn[now] = mn[l] , mu[now] = mu[l] , mv[now] = mv[l];
	if(r && mn[now] > mn[r]) mn[now] = mn[r] , mu[now] = mu[r] , mv[now] = mv[r];
	sz[now] = sz[l] + sz[r];
}

void Build(int &now,int l,int r)
{	
	now=++tot;
	sz[now] = r - l + 1;
	if(l==r){ mu[now]=l,mv[now]=1,mn[now]=a[l]-a[l-1];return; }
	int mid = (l + r) >> 1;
	Build(lc[now],l,mid),Build(rc[now],mid+1,r);
	upd(now);
}
	
void Insert_inloc(int &now,int l,int r,int pos,LL val,int isadd,int ads)
{	
	sz[++tot]=sz[now]+ads,lc[tot]=lc[now],rc[tot]=rc[now],now=tot;
	if(l==r){ mn[now]=isadd*mn[now]+val,mv[now]++;return; }
	int mid = (l+r) >> 1;
	Insert_inloc(lc[now],l,mid,pos,val,isadd,ads),
	Insert_inloc(rc[now],mid+1,r,pos,val,isadd,ads);
	upd(now);
}

void Insert_inrk(int &now,int l,int r,int rk,LL val,int isadd,int ads)
{	
	sz[++tot]=sz[now]+ads,lc[tot]=lc[now],rc[tot]=rc[now],now=tot;
	if(l==r){ mn[now]=isadd*mn[now]+val,mv[now]++;return; }
	int mid = (l+r) >> 1;
	if(rk>sz[lc[now]]) 
		Insert_inrk(rc[now],mid+1,r,rk-sz[lc[now]],val,isadd,ads);
	else 
		Insert_inrk(lc[now],l,mid,rk,val,isadd,ads);
	upd(now);
}
	
void Delete(int &now,int l,int r,int pos)//in location
{	sz[++tot]=sz[now]-(pos-l+1),lc[tot]=lc[now],rc[tot]=rc[now],now=tot;
	if(l==r) return;
	int mid = (l+r) >> 1;
	if(pos > mid) 
		Delete(rc[now],mid+1,r,pos),lc[now]=0;
	else 
		Delete(lc[now],l,mid,pos);
	upd(now);
}
	
int Query_pt(int now,int l,int r,int qsiz)// use rk
{	
	if(l==r) return a[now];
	int mid = (l+r) >> 1;
	if(qsiz>sz[lc[now]]) 
		return Query_pt(rc[now],mid+1,r,qsiz-sz[lc[now]]);
	return Query_pt(lc[now],l,mid,qsiz);
}

int Query_rk(int now,int l,int r,int pos)// use pos
{
	if(l==r) return 1;
	int mid = (l+r) >> 1;
	if(pos <= mid) return Query_rk(lc[now],l,mid,pos);
	return Query_rk(rc[now],mid+1,r,pos)+sz[lc[now]];
}

int Query_val(int now,int l,int r,int rk)// usd rk
{
	if(l==r) return a[now];
	int mid = (l+r) >> 1;
	if(rk>sz[lc[now]]) return Query_val(rc[now],mid+1,r,rk-sz[lc[now]]);
	return Query_val(lc[now],l,mid,rk);
}

int rt[maxn*10],cnt;
LL sum[maxn*10];
priority_queue<pair<LL,int>,vector<pair<LL,int> >,greater<pair<LL,int> > >q;
	
int main()
{
	scanf("%d%d",&n,&k);
	for(int i=1;i<=n;i++) scanf("%d",&a[i]);
	sort(a+1,a+1+n);
	LL base = 0;
	for(int i=1;i<=n;i++) base += 1ll * a[i] * (n-i+1);
	Build(rt[0],1,n);
	q.push(make_pair(base+mn[rt[0]],0)),sum[0]=base;
	printf("%lld\n",base);
	for(k--;k--;)
	{
		printf("%lld\n",q.top().first);
		
		int loc=q.top().second,val=q.top().first,u=mu[rt[loc]],v=mv[rt[loc]];
		q.pop();
		
		Insert_inloc(rt[++cnt]=rt[loc],1,n,u,inf,0,-1);//delete a point
		if(u-v-1>=1) Delete(rt[cnt],1,n,u-v-1);
		Insert_inrk(rt[cnt],1,n,u-v+1,Query_pt(rt[cnt],1,n,u-v+1)-Query_pt(rt[cnt],1,n,u-v),0,0);
		Insert_inrk(rt[cnt],1,n,1,inf,0,0);
		sum[cnt] = val;
		q.push(make_pair(val+mn[rt[cnt]],cnt));
		
		int rku = Query_rk(rt[loc],1,n,u);
		if(rku-v-1>0) 
		{
			Insert_inloc(rt[loc],1,n,u,a[u]-Query_val(rt[loc],1,n,rku-v),1,0);
			q.push(make_pair(sum[loc]+mn[rt[loc]],loc));
		}
	}
}

顺便提供出题人代码一份:

#include<bits/stdc++.h>
#define fo(i,a,b) for(i=a;i<=b;i++)
#define fd(i,a,b) for(i=a;i>=b;i--)
#define max(a,b) (a>b?a:b)
#define min(a,b) (a<b?a:b)
#define mp(a,b) (make_pair(a,b))
using namespace std;
typedef long long ll;
typedef pair<ll,int> pa;
inline int read(){
	int n=0;char c;
	for(c=getchar();c<'0'||c>'9';c=getchar());
	for(;c>='0'&&c<='9';c=getchar()) n=(n<<1)+(n<<3)+(c&15);
	return n;
}
void pint(ll x){
	if (x>=10) pint(x/10);
	putchar(x%10+48);
}
const int N=1e5+5;
const ll inf=1e18;
int i,j,n,k,a[N],cnt,num;
struct ar{
	int l,r,u,v,s;ll g;
}tr[N*150];
struct arr{int now,chu;ll sum;}ro[N];
priority_queue<pa,vector<pa>,greater<pa> >p;
int build(int l,int r){
	int k=++cnt;
	tr[k].s=r-l+1;
	if (l==r) return k;
	int m=(l+r)>>1;
	tr[k].l=build(l,m),tr[k].r=build(m+1,r);
	return k;
}
void update(int k){
	int l=tr[k].l,r=tr[k].r;
	if (tr[l].g<tr[r].g) tr[k].u=tr[l].u,tr[k].v=tr[l].v,tr[k].g=tr[l].g;
	else tr[k].u=tr[l].s+tr[r].u,tr[k].v=tr[r].v,tr[k].g=tr[r].g;
	tr[k].s=tr[l].s+tr[r].s;
}
void ins(int &k,int sum,ll g,int s){
	tr[++cnt]=tr[k],k=cnt;
	if (!tr[k].l&&!tr[k].r){
		tr[k].v++,tr[k].u=tr[k].s=s,tr[k].g+=g;
		return;
	}
	if (tr[tr[k].l].s>=sum) ins(tr[k].l,sum,g,s);
	else sum-=tr[tr[k].l].s,ins(tr[k].r,sum,g,s);
	update(k);
}
void ch(int &k,int sum,ll g,int s){
	tr[++cnt]=tr[k],k=cnt;
	if (!tr[k].l&&!tr[k].r){
		tr[k].u=tr[k].s=s,tr[k].g=g;
		return;
	}
	if (tr[tr[k].l].s>=sum) ch(tr[k].l,sum,g,s);
	else sum-=tr[tr[k].l].s,ch(tr[k].r,sum,g,s);
	update(k);
}
void del(int &k,int sum){
	if (!sum) return;
	tr[++cnt]=tr[k],k=cnt;
	if (tr[tr[k].l].s>sum) del(tr[k].l,sum);
	else sum-=tr[tr[k].l].s,tr[k].l=0,del(tr[k].r,sum);
	update(k);
}
int Q(int k,int l,int r,int sum){
	if (l==r) return a[l];
	int m=(l+r)>>1;
	if (tr[tr[k].l].s>=sum) return Q(tr[k].l,l,m,sum);
	else return Q(tr[k].r,m+1,r,sum-tr[tr[k].l].s);
}
void work(int T){
	int x=ro[T].now;
	ro[++num].sum=ro[T].sum+tr[x].g;
	ro[num].now=ro[T].chu;
	int u=tr[x].u,v=tr[x].v,cn=u-v;
	ch(ro[num].now,u,inf,0);
	if (cn>1) del(ro[num].now,cn-1);
	if (v+1<=tr[ro[num].now].s) ch(ro[num].now,v+1,Q(ro[num].now,1,n,v+1)-Q(ro[num].now,1,n,v),1);
	ch(ro[num].now,1,inf,1);
	ro[num].chu=ro[num].now;
	p.push(mp(ro[num].sum+tr[ro[num].now].g,num));
	if (u>v+1) ins(ro[T].now,u,Q(ro[T].now,1,n,u)-Q(ro[T].now,1,n,u-v-1),1);
	else ins(ro[T].now,u,inf,1);
	p.push(mp(ro[T].sum+tr[ro[T].now].g,T));
}
int main(){
	freopen("permutation.in","r",stdin);
	freopen("permutation.out","w",stdout);
	n=read(),k=read();
	fo(i,1,n) a[i]=read();
	sort(a+1,a+1+n);
	tr[0].g=inf;
	ro[0].now=build(1,n);
	fo(i,1,n){
		ro[0].sum+=(ll)(n-i+1)*a[i];
		if (i>1) ins(ro[0].now,i,a[i]-a[i-1],1);
		else ins(ro[0].now,i,inf,1);
	}ro[0].chu=ro[0].now;
	pint(ro[0].sum),puts("");
	p.push(mp(ro[0].sum+tr[ro[0].now].g,0));
	for(k--;k--;){
		pa now=p.top();p.pop();
		pint(now.first);
		puts("");
		work(now.second);
	}
}

UPD :
终于过了,学习静态差错花了一下午。。。。。。
1.这个idea主要源于 n log n n \log n 的k短路算法,在有多阶段,每个阶段多选择的情况下,可以通过每次拓展两个方向,一个方向是确定选择并到下一阶段,一个方向是考虑次优选择,两个方向并走,k次拓展就可以求出前k小。

2.支持删除插入的可持久化线段树
只要你把位置留好(需要build),操作时类似排名平衡树。

3.AC Code:(内涵海量调试痕迹)

#include<bits/stdc++.h>
#define maxn 100005
#define maxpt maxn*200
#define LL long long
#define inf 1e17
using namespace std;

int n,k;
int lc[maxpt],rc[maxpt],sz[maxpt],mu[maxpt],mv[maxpt],tot;
LL mn[maxpt];
int a[maxn];
inline void upd(int now)
{
	int l = lc[now] , r = rc[now];
	sz[now] = sz[l] + sz[r];
//	printf("%d %d %d\n",now,mn[l],mn[r]);
	if(mn[l] <= mn[r]) mn[now] = mn[l] , mu[now] = mu[l] , mv[now] = mv[l];
	else mn[now] = mn[r] , mu[now] = mu[r] + sz[l] , mv[now] = mv[r];
}

void Build(int &now,int l,int r)
{
	now=++tot;
	if(l==r){mn[now]=(l==1)*inf+a[l]-a[l-1],mu[now]=1,mv[now]=1,sz[now]=1; return;}
	int mid = (l + r) >> 1;
	Build(lc[now] , l , mid);
	Build(rc[now] , mid + 1 , r);
	upd(now);
}

void Delete_pt(int &now,int l,int r,int rk)
{
	sz[++tot]=sz[now],lc[tot]=lc[now],rc[tot]=rc[now],mu[tot]=mu[now],mv[tot]=mv[now],mn[tot]=mn[now],now=tot;
	if(l==r){ mn[now]=inf,sz[now]=0;return; }
	int mid = (l+r) >> 1;
	if(rk<=sz[lc[now]]) Delete_pt(lc[now],l,mid,rk);
	else Delete_pt(rc[now],mid+1,r,rk-sz[lc[now]]);
	upd(now);
//	printf("%d %d\n",now,sz[now]);
}

void Change(int &now,int l,int r,int rk,LL newval,int newlen)
{
	sz[++tot]=sz[now],lc[tot]=lc[now],rc[tot]=rc[now],mu[tot]=mu[now],mv[tot]=mv[now],mn[tot]=mn[now],now=tot;
	if(l==r){ mn[now]=newval,mv[now]=newlen;return; }
	int mid = (l+r) >> 1;
	if(rk <= sz[lc[now]]) Change(lc[now],l,mid,rk,newval,newlen);	
	else Change(rc[now],mid+1,r,rk-sz[lc[now]],newval,newlen);
	upd(now);
//	printf("%d %lld %d %d\n",now,mn[now],lc[now],rc[now]);
}

void Add(int &now,int l,int r,int rk,LL newval,int newlen)
{
	sz[++tot]=sz[now],lc[tot]=lc[now],rc[tot]=rc[now],mu[tot]=mu[now],mv[tot]=mv[now],mn[tot]=mn[now],now=tot;
	if(l==r)
	{ 
		mn[now]+=newval;
		mv[now]=newlen;return; }
	int mid = (l+r) >> 1;
	if(rk <= sz[lc[now]]) Add(lc[now],l,mid,rk,newval,newlen);	
	else Add(rc[now],mid+1,r,rk-sz[lc[now]],newval,newlen);
	upd(now);
}

void Delete(int &now,int l,int r,int rk)
{
	if(!rk) return;
	sz[++tot]=sz[now],lc[tot]=lc[now],rc[tot]=rc[now],mu[tot]=mu[now],mv[tot]=mv[now],mn[tot]=mn[now],now=tot;
	int mid = (l+r)>>1;
	if(rk<sz[lc[now]]) Delete(lc[now],l,mid,rk);
	else Delete(rc[now],mid+1,r,rk-sz[lc[now]]),lc[now]=0;
	upd(now);
}

int Query(int &now,int l,int r,int rk)
{
	if(l==r) return a[l];
	int mid = (l+r) >> 1;
	if(rk <= sz[lc[now]]) return Query(lc[now],l,mid,rk);
	return Query(rc[now],mid+1,r,rk-sz[lc[now]]);
}

int rt[maxn*5],churt[maxn*5],cnt;
priority_queue<pair<LL,int>,vector<pair<LL,int> >,greater<pair<LL,int> > >q;

int main()
{
	
	freopen("permutation.in","r",stdin);
	freopen("permutation.out","w",stdout); 
	
	mn[0] = inf;
	scanf("%d%d",&n,&k);
	for(int i=1;i<=n;i++) scanf("%d",&a[i]);
	sort(a+1,a+1+n);
	LL base = 0;
	for(int i=1;i<=n;i++) base += 1ll * a[i] * (n-i+1);
	Build(rt[0],1,n);
	q.push(make_pair(base+mn[rt[0]],0));
	churt[0] = rt[0];
	printf("%lld\n",base);
	
	for(k--;k--;)
	{
		LL val = q.top().first;
		int  loc = q.top().second;
		LL preval = val - mn[rt[loc]]; 
		
	//	printf("%d\n",loc);
		
		q.pop();
		printf("%lld\n",val);
		int u = mu[rt[loc]] , v = mv[rt[loc]];
	//	printf("%d\n",rt[loc]);
	//	printf("%d %d\n",u,v);
	//	printf("%d\n",sz[rt[loc]]);
		rt[++cnt] = churt[loc];
	//	printf("%d %d %d %d\n",v+1,sz[rt[cnt]],mn[rt[cnt]],mn[rc[rt[cnt]]]);
		Delete_pt(rt[cnt],1,n,u); // Delete a point
	//	printf("%d\n",sz[rt[cnt]]);
	//	printf("\n@%lld\n",u-v-1);
	//	printf("%d\n",mn[rt[cnt]]);
	//	printf("%d %d %d %d\n",v+1,sz[rt[cnt]],mn[rt[cnt]],mn[rc[rt[cnt]]]);
		if(u-v-1>=1) 
			Delete(rt[cnt],1,n,u-v-1); // Delete an interval in the front
	//	printf("\n@%lld\n",val+mn[rt[cnt]]);
	//	printf("%d\n",mn[rt[cnt]]);
	//	printf("%d %d %d %d\n",v+1,sz[rt[cnt]],mn[rt[cnt]],mn[rc[rt[cnt]]]);
		if(v+1<=sz[rt[cnt]]) 
			Change(rt[cnt],1,n,v+1,Query(rt[cnt],1,n,v+1)-Query(rt[cnt],1,n,v),1);// reset an extending plan
		Change(rt[cnt],1,n,1,inf,1);// reset an extending plan
	//	printf("%d\n",mn[rt[cnt]]);
		q.push(make_pair(val+mn[rt[cnt]],cnt));
		churt[cnt] = rt[cnt];
	//	printf("%d %d %d %d\n",rt[cnt],mn[rc[rt[cnt]]],mu[rc[rt[cnt]]],mv[rc[rt[cnt]]]);
	//	printf("%d %d %d\n",mn[rc[rt[cnt]]],mu[rc[rt[cnt]]],mv[rc[rt[cnt]]]);
	//	printf("\n@%lld\n",val+mn[rt[cnt]]);
	
		Add(rt[loc],1,n,u,u-v-1>=1?Query(rt[loc],1,n,u)-Query(rt[loc],1,n,u-v-1):inf,v+1);
		q.push(make_pair(preval+mn[rt[loc]],loc));
	//	printf("\n#%lld\n",preval+mn[rt[loc]]);
	}
}

猜你喜欢

转载自blog.csdn.net/qq_35950004/article/details/85958458
今日推荐