CF1374E2 Reading Books(hard version) 题解
CF1374E2 Reading Books(hard version)
update:这道题和 CF799E 完全就是一道题,双倍经验。
这道题是在 CF1374E1 Reading Books(easy version) 的基础上出的,而且仅仅增加了一个
题意简述:
有
思路:
简单版
先不考虑
困难版
和简单版唯一的不同在于,简单版可以读任意数量的书,而困难版必须读
我们来分析一下刚才过程中,未选择的书有哪些。还是设选择
中有 本未被选择; 中有 本书未被选择; 中有 本书未被选择; 中的我们都没有选。
我们设没有选的书的集合为
我们来理一下这个过程。首先从小到大枚举选
至于输出方案,首先
总时间复杂度
#include<bits/stdc++.h> #define ll long long using namespace std; const int N = 2e5+100; inline int read(){ int x = 0; char ch = getchar(); while(ch<'0' || ch>'9') ch = getchar(); while(ch>='0'&&ch<='9') x = x*10+ch-48, ch = getchar(); return x; } struct xwx { ll val; int id;int v; bool operator < (const xwx &b ) const{ return val < b.val; } };// 用来存储 a、b、ab、none 中的书 struct qwq{ int v, id; bool operator <(const qwq &b) const{ if(v == b.v){ return id < b.id; } return v < b.v; } bool operator >(const qwq &b) const{ if(v == b.v){ return id > b.id; } return v >b.v; } };//作为 FHQ 上元素的权值,重载运算符便于删除操作。 xwx a[N], b[N], ab[N], no[N];//A喜欢,B喜欢,都喜欢,都不喜欢。 struct node{ qwq val; ll sum;//sum 记录子树内的权值和。 int siz; int ls, rs; int rnd; }; int root; mt19937 getrand(time(0)); struct FHQ_Treap{ node tree[N]; int idx; #define ls(x) tree[x].ls #define rs(x) tree[x].rs void push_up(int tr){ tree[tr].sum = tree[ls(tr)].sum+tree[rs(tr)].sum+tree[tr].val.v; tree[tr].siz = tree[ls(tr)].siz+tree[rs(tr)].siz+1; } int New(qwq val){ tree[++idx] = (node){val, val.v, 1, 0, 0, (int)getrand()}; return idx; } void split_by_size(int pos, int &l, int &r, int K){//按大小分裂,也就是取出前 K 小的元素 if(!pos) return l = r = 0, void(); int tmp = tree[ls(pos)].siz+1; if(K>=tmp){ l = pos; split_by_size(rs(pos), rs(pos), r, K-tmp); push_up(l); } else{ r = pos; split_by_size(ls(pos), l, ls(pos), K); push_up(r); } } void split_by_val(int pos, int &l, int &r, qwq K){//按权值大小分裂,主要用于插入元素。 if(!pos) return l = r = 0, void(); if(K>tree[pos].val){ l = pos; split_by_val(rs(pos), rs(pos), r, K); push_up(l); } else{ r = pos; split_by_val(ls(pos), l, ls(pos), K); push_up(r); } } int merge(int l, int r) { if((!l) || (!r)) return l | r; if(tree[l].rnd < tree[r].rnd){ rs(l) = merge(rs(l), r); push_up(l); return l; } else{ ls(r) = merge(l, ls(r)); push_up(r); return r; } } void insert(int val, int id){ int dl, dr; split_by_val(root, dl, dr, (qwq){val, id}); root = merge(merge(dl, New((qwq){val, id})), dr); } void del(int val, int id){ int dl, md, dr; split_by_val(root, dl, dr, (qwq){val, id}); split_by_val(dr, md, dr, (qwq){val, id+1});//将目标节点分裂出来,合并两边。 root = merge(dl, dr); } ll query(int K){ int dl, dr; split_by_size(root, dl, dr, K);//分裂出前 K 小的元素。 ll tmp = tree[dl].sum; root = merge(dl,dr); return tmp; } void clear(){ root = 0; idx = 0; memset(tree, 0, sizeof(tree)); } void output(int pos){ if(ls(pos)){ output(ls(pos)); } printf("%d ", tree[pos].val.id); if(rs(pos)){ output(rs(pos)); } }//遍历子树,输出方案 void print(int K){ int dl,dr; split_by_size(root, dl, dr, K); if(dl) output(dl); } }s; int ta, tb, tab, tn; int n, m, K; int main(){ n = read(), m = read(), K = read(); for(int i = 1, ar, br, t; i<=n; ++i){ t = read(), ar = read(), br = read(); if(ar && br){ ab[++tab] = (xwx){t, i, t}; s.insert(t, i); } else if(ar){ a[++ta] = (xwx){t, i, t}; } else if(br){ b[++tb] = (xwx){t, i, t}; } else{ no[++tn] = (xwx){t, i, t}; s.insert(t, i);//没人喜欢的直接插入到 S 里 } } sort(a+1, a+ta+1); sort(b+1, b+tb+1); sort(ab+1, ab+tab+1); // sort(no+1, no+tn+1); for(int i = 1; i<=ta; ++i){ a[i].val+=a[i-1].val; } for(int i = 1; i<=tb; ++i){ b[i].val+=b[i-1].val; } for(int i = 1; i<=tab; ++i){ ab[i].val+=ab[i-1].val; } // 求前缀和。 ll ans = 0x3f3f3f3f3f3f3f3f; int num = -1;//记录答案在哪里产生 int tota = ta, totb = tb, totab = tab; for(int i = 0; i<=min(K, tab); ++i){ if(i > 0){ s.del(ab[i].v, ab[i].id); }//枚举到的 ab 必须要选,所以就从 S 中删除。 if((K-i)>ta || (K-i)>tb || K*2-i > m) continue; while(ta > (K-i)){ s.insert(a[ta].v, a[ta].id); --ta; } while(tb > (K-i)){ s.insert(b[tb].v, b[tb].id); --tb; }//将选不上的 a 和 b 都加入 S ll tmp = ab[i].val + a[ta].val + b[tb].val + s.query(m-K*2+i); if(ans > tmp){ num = i; ans = tmp; } } if(ans == 0x3f3f3f3f3f3f3f3f){ puts("-1"); return 0; } printf("%lld\n", ans); s.clear(); for(int i = num+1; i<=totab; ++i){ s.insert(ab[i].v, ab[i].id); } for(int i = (K-num+1); i<=tota; ++i){ s.insert(a[i].v, a[i].id); } for(int i = (K-num+1); i<=totb; ++i){ s.insert(b[i].v, b[i].id); } for(int i = 1; i<=tn; ++i){ s.insert(no[i].v, no[i].id); }//将 S 恢复到 i = num 时的状态 for(int i = 1; i<=num; ++i){ printf("%d ", ab[i].id); } for(int i = 1; i<=K-num; ++i){ printf("%d ", a[i].id); } for(int i = 1; i<=K-num; ++i){ printf("%d ", b[i].id); }//顺序输出 ab、a、b s.print(m-K*2+num);//输出 S 中选择的元素。 return 0; }