给出 n 个数
定义排列一个 1~n 的排列 P 的价值为:
请你给出排列价值前 k 小的 k 个排列的价值。
友情提示,博主已被此题搅得神志不清,下文内容结构请选择和代码相近,和真理相通的部分自行判断并使用。
一个套路:如果我们每次拓展都有n种选择,那么可以用一个优先队列取出答案最小的候选方案,然后用它拓展生成新的方案,如果拓展出的方案能不重不漏,那么第k个就是第k小方案,拓展新的方案时,可以用:1.拓展最优的下一步方案。2.换当前这步的方案为次优方案。这样可以证明第k个就是第k小方案,但是我们只要快速求出最优和次优和次次优。。。。。就可以在低于nk的时间复杂度内求出k小答案。
这个题就第i次拓展是将 ,依次和前面的换,换v次,可以发现换v次优于换v+1次,那么就可以快速求出次优。。。。。。再用一个线段树维护最优的下一个i(你可以暂时理解为下一个i大于当前i,upd:但是当你调程序到醉生欲死的时候你会发现这个并不完全正确,因为程序实现的原因,你不能就这么按从左到右的顺序拓展,偶尔也需要往回看,更深一层的理解的就是将你的第i次拓展看做第i个阶段,然后你有 个方案【对于每个 有 个v】,发现 相等的情况下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主要源于
的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]]);
}
}