1. 程式人生 > >【可持久化線段樹】2019雅禮集訓 permutation

【可持久化線段樹】2019雅禮集訓 permutation

題目:

在這裡插入圖片描述


分析:

比較噁心的一道題。
首先,將式子轉化一下,變成
A P i ( n

i + 1 ) \sum A_{P_i}*(n-i+1)
這裡應該很好理解,無非就是把當前排列倒序,然後計算貢獻而已。

但是隻有這麼寫才比較容易看出下一步的性質:
顯然,題目的最小值,必然是當 P

i P_i 為升序時(否則可以通過交換一對來得到更小的答案)

然後對某個排列,轉移到任意其他排列,可以通過這種方式:
將目標排列的第一個數,從當前排列中移動到第一位,再講目標排列的第二個數,從當前排列中移動到第二位……

顯然,每次這樣做會使得答案不減。(每次操作後的貢獻為 j

i 1 ( A i A j ) , A i > A j \sum_{j}^{i-1}(A_i-A_j),又A_i>A_j

所以,可以把這看做是轉移的方式。

定義 g ( u , l e n ) g(u,len) 表示在當前狀態下,把u向前移動len的貢獻。

由於 g ( u , l e n ) 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 ( u , l e n ) g(u,len)

首先,肯定能轉移到 g ( u , l e n + 1 ) g(u,len+1) ,其含義為:在原序列中,將u移動len+1位。
然後,還能轉移到 g ( u i , l e n i ) g(u_i,len_i) ,其中 ( i [ u l e n + 1 , n ] ) (i\in[u-len+1,n]) ,其含義為:在移動了u後,再移動其餘位置的轉移。

第一種轉移很好操作,直接線段樹上更改就好了。

問題是第二種怎麼搞。

觀察到,第二種轉移方式,無非就只有 u l e n u-len 及以前的被刪掉了,然後 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[
            
           

相關推薦

持久化線段2019集訓 permutation

題目: 分析: 比較噁心的一道題。 首先,將式子轉化一下,變成 ∑ A

線段2019集訓 sequence

題目: 給出k,和一個長度為n的序列A 有q次詢問, 每次詢問 A l

2019集訓持久化線段模型轉化D1T2Permutation

目錄 題意 輸入格式 輸出格式 思路 程式碼 題意 給定一個長度為n的序列A[],你需要確定一個長度為n的排列P[],定義當前排列的值為: \[\sum_{i=1}^{n}{A[i]P[i]}\] 現在給定一個整數k,需要你求出,在所有可能的排列中(顯然有n!種),最小的k個"

[bzoj4504]K個串持久化線段

【題目連結】    【題解】   首先記下每個點向右所控制的區間,就是它到下一個與它相同的位置-1。   我們考慮對於每個左端點維護一棵線段樹下標表示以該點為右端點的區間的答案。   那麼左端點為1的區間可以O(N)O(N)暴力求出。   對於兩個相

BZOJ5338 [TJOI2018] Xor 持久化Triedfs序

%d print num color style printf second 進行 AS 題目分析:   很無聊的一道題目。首先區間內單點對應異或值的詢問容易想到trie樹。由於題目在樹上進行,case1將路徑分成兩段,然後dfs的時候順便可持久化trie樹做詢問。case

HDU 6191Query on A Tree 持久化字典

algorithm stream %d ons turn class img cstring str 題目 給出一棵有n個結點的樹,樹根是1,每個結點給出一個value。然後給出q個詢問,每個詢問給出兩個整數u和x,你要在以u結點為根的子樹中找出一個結點v,使得val

Trie啟發式合併2019集訓 matrix

題目: 定義一個矩陣的貢獻為:其互不相同的行的種類數。 給出一個矩陣,求其所有子矩陣的貢獻和。 分析: 可以把每一行拿出來,弄成一個字串,建一顆Trie樹出來。 此時,就可以算出以最左端為左邊界的所有子矩陣的貢獻。 算完後,把第一層節點合併,相當於去除了第一列的

費用流求證明2019集訓 sum

題目: 在不超過n的數中,選出一個集合,使得集合內部元素兩兩互質,且集合元素總和儘可能大。 分析: 神奇的性質題。 出題人不會證,問了一堆大佬也不會。 哈(那麼證明就留給讀者來完成) 性質是: 1、每個元素最多隻有兩個質因子。 2、若有兩個質因子,一定有一個大

點分治FFT2019集訓 bracket

題目: 分析: 太過套路這裡就簡單說說 顯然,當詢問數k很小時,就是個裸的點分治題。 現在k變大了。那就FFT算貢獻。 #include<cstdio> #include<cstring> #include<cmath> #in

矩陣加速數學2019集訓 math

題目: 簡單地說,就是對於n的每一個長度為m的劃分( k i

[Luogu 3919]模板持久化數組(持久化線段/平衡

ins eset blog sta -s ctime it is put tex Description 如題,你需要維護這樣的一個長度為 N 的數組,支持如下幾種操作 在某個歷史版本上修改某一個位置上的值 訪問某個歷史版本上的某一位置的值 此外,每

BZOJ4704旅行 鏈剖分+持久化線段

-s 編號 不能 ets include 出發 oot %d rip 【BZOJ4704】旅行 Description 在Berland,有n個城堡。每個城堡恰好屬於一個領主。不同的城堡屬於不同的領主。在所有領主中有一個是國王,其他的每個領主都直接隸屬於另一位領主,

BZOJ3681Arietta 鏈剖分+持久化線段優化建圖+網絡流

des 持久化 -s 過程 void 但是 陽光 建圖 == 【BZOJ3681】Arietta Description Arietta 的命運與她的妹妹不同,在她的妹妹已經走進學院的時候,她仍然留在山村中。但是她從未停止過和戀人 Velding 的書信往來。一天,

模板持久化線段 1(主席

base math 一次 數據 mar 指定 das min 第k小 題目背景 這是個非常經典的主席樹入門題——靜態區間第K小 數據已經過加強,請使用主席樹。同時請註意常數優化 題目描述 如題,給定N個正整數構成的序列,將對於指定的閉區間

洛谷P3402 模板持久化並查集(持久化線段線段

std 樹節點 https case 深度 build eof spa 復雜度 orz TPLY 巨佬,題解講的挺好的。 這裏重點梳理一下思路,做一個小小的補充吧。 寫可持久化線段樹,葉子節點維護每個位置的fa,利用每次只更新一個節點的特性,每次插入\(logN\)個節點,

[解題報告]P3919 模板持久化數組(持久化線段/平衡

版本 持久化 完全 直接 n) ace 思路 efi mes 題目簡述 維護一個長度為N的數組,支持如下幾種操作: 在某個歷史版本上修改某一個位置上的值 訪問某個歷史版本上的某一位置的值 此外,每進行一次操作(對於操作2,即為生成一個完全一樣的版本,不作任何改動),就會

XSY2720區間第k小 整體二分 持久化線段

cpp markdown 區間 序列 printf line 線段 using back 題目描述   給你你個序列,每次求區間第\(k\)小的數。   本題中,如果一個數在詢問區間中出現了超過\(w\)次,那麽就把這個數視為\(n\)。   強制在線。   \(n\leq

XSY2732Decalcomania 持久化線段 分治

lin %d == void ret 多少 for size include 題目描述   有一個陶瓷瓶周圍有\(n\)個可以印花的位置。第\(i\)個與第\(i+1\)個位置之間的距離為\(d_i\),在第\(i\)個位置印圖案要\(t_i\)秒。   機器剛開始在\(0

9018:2208持久化線段2

body stat lin nbsp return == tro mst tdi 2208: 【模板】可持久化線段樹2 時間限制: 3 Sec 內存限制: 256 MB提交: 30 解決: 12[提交][狀態][討論版] 題目描述 靜態區間第K小問題是典型的主席樹模板

9018:2207持久化線段1

正整數 page sta 輸出 amp status efi oid 所有 2207: 【模板】可持久化線段樹1 時間限制: 2 Sec 內存限制: 256 MB提交: 45 解決: 14[提交][狀態][討論版] 題目描述 你需要維護1個數列的若幹版本: 對於給定的