題意:有n個任務。每個任務描述爲(s,e,p)表示起始、結束時間、優先級。M次詢問,查詢i時刻優先級排前k的任務的優先級之和。如果k超過那個時刻運行任務的總量,輸出那個時刻所以優先級之和。強制在線。
s,e<=10w, p<=1000w, n,m<=10w.
哎呀我去這麼裸的主席樹爲什麼去年省選只有一個人A了?
開始看做插入一個數,結束看做刪除,詢問就是問第i個版本中前k個之和。先把p離散化,然後掃描一遍用主席樹來維護p值。好像沒什麼坑點。。
不想寫離散化。。這兩天玩treap有點玩上火了,就寫個可持久化平衡樹吧2333
事實證明之前沒寫過的東西直接拿來做題對我這種傻叉來說比較蛋疼。。不過好在這道題實在是太裸了還是在三個小時以內把它調出來了(主要是沒實現過平衡樹的可持久化呀QAQ大部分時間檢查模板去了)。
由於要可持久化,基於旋轉的平衡樹是要死的(有指針間接指向待修改節點的節點統統要修改,每次旋轉帶來的新建節點是O(logn)的,期望的總旋轉次數爲O(mlogn),所以無論是時間還是空間都會退化爲O(mlog^2n)不太和諧)。而非旋轉式treap一次修改期望新建節點是O(logn)的。插入和刪除都是基於一次get_rank加上總共三次split/merge實現,常數比較大,而且空間對常數比時間對常數敏感得多,所以可持久化treap對空間需求量比主席樹大一點。所以如果不是內存限制512MB或者1024MB最好不要用可持久化treap而是儘量用離散化+主席樹(不過貌似gmr大神掌握了不用離散的寫法)。
有一些技巧,一開始如果有一個較長的初始序列的話,建初始序列的時候可以不用函數式,直接建即可,省不少內存。但這題上體現不明顯。還有就是不用保存修正值,可以直接用clj說的隨機啓發式合併。還有貌似這個模板由於種種巧妙的地方可以直接對空樹進行merge來插入而不需要任何特判,所以如果之前一個版本是空節點的話就不要copy之前的根了否則會多出來一個權值爲0的節點影響答案。
注意merge的時候要返回新建的節點而不是原來的節點!!坑了我一個小時!!
#include<iostream>
#include<cassert>
#include<cstdio>
#include<cstring>
#include<vector>
#include<algorithm>
#define rep(i,a,b) for(int i=a;i<=b;++i)
#define erp(i,a,b) for(int i=a;i>=b;--i)
#define fi first
#define se second
#define LL long long
using namespace std;
const int MAXN = 100002;
const int MAXS = MAXN * 100;
typedef pair<int, int> pii;
int N, M;
vector< pii > tab[MAXN];
inline int ran()
{
static int sd = 1237;
return sd = (sd*12371237)&0x7fffffff;
}
int rw[MAXN]; //每天多少任務
struct Treap
{
int lch[MAXS], rch[MAXS], sz[MAXS], val[MAXS];
LL sum[MAXS];
int ncnt, rt[MAXN];
Treap() { ncnt = rt[0] = 0; }
inline void pushup(int x)
{
sz[x] = sz[lch[x]] + sz[rch[x]] + 1;
sum[x] = sum[lch[x]] + sum[rch[x]] + val[x];
}
int NewNode(int i = 0)
{
++ncnt;
val[ncnt] = sum[ncnt] = i;
sz[ncnt] = 1;
return ncnt;
}
int CopyNode(int x)
{
++ncnt;
lch[ncnt] = lch[x];
rch[ncnt] = rch[x];
sz[ncnt] = sz[x];
val[ncnt] = val[x];
sum[ncnt] = sum[x];
return ncnt;
}
inline bool rand_cmp(int a, int b)
{
return ran()%(sz[a]+sz[b]) < sz[a];
}
int merge(int a, int b)
{
if (!a || !b) return a+b;
int r;
if (rand_cmp(a, b))
{
r = CopyNode(a);
rch[r] = merge(rch[a], b);
}
else
{
r = CopyNode(b);
lch[r] = merge(a, lch[b]);
}
return pushup(r), r;
}
pii split(int x, int k)
{
if (!x) return pii(0, 0);
pii y;
int r;
if (sz[lch[x]] >= k)
{
y = split(lch[x], k);
r = CopyNode(x);
lch[r] = y.se, y.se = r;
}
else
{
y = split(rch[x], k - sz[lch[x]] - 1);
r = CopyNode(x);
rch[r] = y.fi, y.fi = r;
}
return pushup(r), y;
}
int getRank(int x, const int&v)
{
if (!x) return 0;
if (v < val[x]) return getRank(lch[x], v);
return sz[lch[x]] + 1 + getRank(rch[x], v);
}
void FuncIns(int&r, const int&v)
{
int k = getRank(r, v);
pii t = split(r, k);
int x = NewNode(v);
r = merge(merge(t.fi, x), t.se);
}
void FuncDel(int&r, const int&v)
{
int k = getRank(r, v);
pii t1 = split(r, k-1);
pii t2 = split(t1.se, 1);
r = merge(t1.fi, t2.se);
}
LL qsum(int x, int k) //前k大的和
{
if (!x || !k) return 0;
if (k <= sz[lch[x]]) return qsum(lch[x], k);
return sum[lch[x]] + val[x] + qsum(rch[x], k - sz[lch[x]] - 1);
}
void dealDay(int d)
{
if (sz[rt[d-1]] != 0)
rt[d] = CopyNode(rt[d - 1]);
rep(i, 0, (int)tab[d].size()-1)
{
int op = tab[d][i].fi, x = tab[d][i].se;
if (op == 1) FuncIns(rt[d], x);
else FuncDel(rt[d], x);
}
}
LL quary(int d, int k)
{
if (k >= rw[d]) return sum[rt[d]];
return qsum(rt[d], k);
}
} tp;
LL pre = 1;
int main()
{
scanf("%d%d", &N, &M);
int s, e, p, x, a, b, c, k;
rep(i, 1, N)
{
scanf("%d%d%d", &s, &e, &p);
rw[s]++, rw[e+1]--;
tab[s].push_back(pii(1, p));
tab[e+1].push_back(pii(-1, p));
}
rep(i, 1, M)
{
rw[i] += rw[i-1];
tp.dealDay(i);
}
while (M --)
{
scanf("%d%d%d%d", &x, &a, &b, &c);
k = 1 + (pre*a+b)%c;
pre = tp.quary(x, k);
cout << pre << '\n';
}
return 0;
}