1 条题解

  • 0
    @ 2026-10-19 1:55:09

    [NOI 2004] 郁闷的出纳员

    知识点

    树状数组、离线离散化、全局偏移、顺序统计

    解题思路

    所有员工工资减去全局偏移量后保持不变。先读完命令,计算每次插入的归一化工资并离散化;回放时用树状数组保存计数。加减工资只改偏移,扣薪后删除低于下界的值;第k高转为第总数-k+1小,用树状数组查找。

    正确性说明

    归一化工资+偏移量始终等于实际工资,因此集体调整不必逐个改数。离散化只改变顺序编号,不改变工资比较;删除阈值以下的全部计数恰对应离职员工。树状数组前缀和给出排名,二进制定位可恢复第k个工资。初始未达标者不入树也不计离职,满足题意。

    易错点

    低于下界才离职,等于下界保留;忽略初始不达标者;允许0条命令;F的名次超过人数输出-1;偏移和归一化用64位。

    复杂度

    O(n log n)时间,O(n)空间;每次删除的一组员工均来自此前插入,删除总量有界。

    参考代码(GNU C++17)

    #include<bits/stdc++.h>
    using namespace std;
    struct BIT{int n;vector<int>t;BIT(int n):n(n),t(n+1){}void add(int x,int v){for(;x<=n;x+=x&-x)t[x]+=v;}int kth(int k){int x=0;for(int step=1<<20;step;step>>=1)if(x+step<=n&&t[x+step]<k)k-=t[x+=step];return x+1;}};int main(){ios::sync_with_stdio(false);cin.tie(nullptr);int n;long long low;cin>>n>>low;vector<pair<char,long long>>ops(n);vector<long long>coords;long long off=0;for(auto&[op,k]:ops){cin>>op>>k;if(op=='I'&&k>=low)coords.push_back(k-off);else if(op=='A')off+=k;else if(op=='S')off-=k;}sort(coords.begin(),coords.end());coords.erase(unique(coords.begin(),coords.end()),coords.end());BIT bit(coords.size());vector<int>freq(coords.size()+1);int total=0,left=0;off=0;for(auto[op,k]:ops){if(op=='I'){if(k<low)continue;int idx=lower_bound(coords.begin(),coords.end(),k-off)-coords.begin()+1;bit.add(idx,1);freq[idx]++;total++;}else if(op=='A')off+=k;else if(op=='S'){off-=k;while(total){int idx=bit.kth(1);if(coords[idx-1]+off>=low)break;int count=freq[idx];freq[idx]=0;bit.add(idx,-count);total-=count;left+=count;}}else{if(k>total)cout<<-1<<'\n';else cout<<coords[bit.kth(total-(int)k+1)-1]+off<<'\n';}}cout<<left<<'\n';}
    
    • 1

    信息

    ID
    914
    时间
    1000ms
    内存
    125MiB
    难度
    普及+
    标签
    递交数
    0
    已通过
    0
    上传者