1. 程式人生 > >雅禮集訓 wc2019 Day1 T2 permutation

雅禮集訓 wc2019 Day1 T2 permutation

給出 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