首先如果沒有出現次數的限制的話,這題就是超級鋼琴
但由於有了這個限制,不能簡單地用字首和
考慮順著做的時候每個點的貢獻,如果a[i]=x,x上次出現位置是lst[x](可以用乙個map來記),那它會給右端點為[i,n],左端點為[lst[x]+1,i]的區間帶來x的貢獻
根據szr巨佬的說法,主席樹的本質就是字首和套線段樹,所以我們如果按區間的右端點建主席樹的根,每顆線段樹內部存每個位置作為左端點的最大值,只需要給root[i]這棵樹上[lst[x]+1,i]做區間+=x就可以了
而且i後面的位置的樹還沒有建出來,所以不用擔心修改以後的更新問題
那麼主席樹上怎麼區間修改呢...對於這道題,只需要像正常的線段樹一樣pushdown,update,但是每次修改子節點的時候都是新開乙個點,然後把資訊拷貝過去再修改,在連過去,防止改到前面線段樹上的值
然後開優先佇列,記下來(x,v,l,r,m)表示右端點為x,在[l,r]的範圍內最大值是v,在m取到,每次取出來隊頂,然後分割成(x,v',l,m-1,m')和(x,v",m+1,r,m")再塞回佇列裡,這樣做k-1次,最後的隊頂就是答案
時空複雜度大概都是$o(nlogn)$的,空間要開大一點。
1 #include2#define pa pair3
#define clr(a,x) memset(a,x,sizeof(a))
4using
namespace
std;
5 typedef long
long
ll;6
const
int maxn=1e5+10,maxp=maxn*100;7
const ll inf=1e18;89
inline ll rd()
12while(c>='
0'&&c<='
9') x=x*10+c-'
0',c=getchar();
13return x*neg;14}
1516
struct
node
21};
22bool
operator
< (node a,node b)
2324 ll laz[maxp];int ch[maxp][2
],root[maxn],pct;
25pa v[maxp];
26int
n,k;
27 maplst;
28 priority_queueq;
2930 inline void update(int
p)33 inline int add(int
p,ll y)
40 inline void pushdown(int
p)46
47void build(int &p,int l,int
r)56}57
58void insert(int &p,int pre,int l,int r,int x,int y,int
z)else69}
7071 pa query(int p,int l,int r,int x,int
y)81}82
83int
main()
97for(i=1;i)
105 printf("
%lld\n
",q.top().v);
106return0;
107 }
BZOJ4504 主席樹 K個串
只區間出現一次的數就是上一次出現在位置在區間外的數,這個可以用主席樹實現,然後套用超級鋼琴的思想就可以了。include include include include include using namespace std const int n 100010 typedef long long ...
UVA11997 K個最小和 優先佇列 K路合併
有k個整數陣列,包含k個元素。在每個陣列中取乙個元素加起來,可以得到k k個和。求這些和中最小的k個值 分析 這題有簡化版本的,即2個整數陣列a,b,包含k個元素,在每個陣列中取乙個元素加起來,可以得到k 2個和,求這些和中最小的k個值。我們需要把這k 2個和組織成如下k個有序表.表1 a1 b1 ...
最熱門的K個搜尋串
11073 最熱門的k個搜尋串 時間限制 350ms 記憶體限制 65535k 提交次數 0 通過次數 0 語言 not limited 描述大家都非常喜歡而習慣用baidu,google,sogou等搜尋引擎來搜尋自己感興趣的資料。搜尋引擎會通過日誌檔案把使用者每次檢索使用的所有檢索串都記錄下來,...