[TJOI2019] 甲苯先生的滚榜 神奇做法
do_it_tomorrow · · 题解
注意到数据随机,所以考虑乱搞。
因为 vector 的常数非常的小,所以一个很显然的思路就是直接在 vector 里面去 erase、insert 和 lower_bound。
发现过不了,考虑优化。
因为在 vector 中进行前两个操作的时间与 vector 的长度有很大关系,所以容易想到减小 vector 的长度。
考虑开 vector,第
对于查询,先在 vector 中二分,然后再查询的 AC 数比自己大的就行了。
对于这个后缀和操作,容易想到用树状数组维护,在发布题解时是最优解。
考虑对于一次操作,最劣的情况肯定是将所有的数全部添加到 vector 中之后在把这些元素全部删除。
这样的操作显然只能填满 vector,而因为 vector 有
所以最终的时间复杂度我为
所以最终的计算次数大概只有
然而数据是随机的,所以实际上的复杂度还会除以
在最后由衷的感谢 DengDuck,ta 的帮助让我的题解蓬荜生辉,请关注 DengDuck 谢谢。
参考资料: https://www.luogu.com.cn/article/9yp9o90m
#include<bits/stdc++.h>
using namespace std;
const int N=1e6+5;
unsigned int n,last=7,seed;
int m,a[N],b[N];
unsigned int get(){ seed=seed*17+last;return seed%n+1; }
vector<int> v[N];
struct BIT{
int s[N];
int lowbit(int x){return x&-x;}
void updata(int x,int v){for(int i=x;i>=1;i-=lowbit(i)) s[i]+=v;}
int ask(int x){int ans=0;for(int i=x;i<N;i+=lowbit(i)) ans+=s[i];return ans;}
void clear(){memset(s,0,n*4+5);}
}tr;
void solve(){
cin>>n>>m>>seed,tr.clear();
for(int i=0;i<=m;i++) v[i].clear();
memset(a,0,n*4+5),memset(b,0,n*4+5);
for(int i=1;i<=m;i++){
int x=get(),y=get();
if(a[x]) v[a[x]].erase(lower_bound(v[a[x]].begin(),v[a[x]].end(),b[x])),tr.updata(a[x],-1);
b[x]+=y,a[x]++;
auto it=lower_bound(v[a[x]].begin(),v[a[x]].end(),b[x]);
cout<<(last=it-v[a[x]].begin()+tr.ask(a[x]+1))<<'\n';
tr.updata(a[x],1),v[a[x]].insert(it,b[x]);
}
}
int main(){
ios::sync_with_stdio(false);
cin.tie(nullptr);
int T;cin>>T;
while(T--) solve();
return 0;
}