[Ynoi2016] 掉进兔子洞

题目链接 : [Ynoi2016] 掉进兔子洞

好不容易争取来的三天没有模拟赛,不打Ynoi打什么。

先从莫队+bitset的板板开始,第一个自己打出来的Ynoi捏。

一个套路,就是将每个数离散化后的值变成小于等于它的数的个数,然后就可以用bitset记录出现次数了。

具体的操作就是,假如一个数\(x\)出现了\(cnt_x\)次,那么它第\(cnt_x\)次在一个bitset的下标中就是\(x-cnt_x+1\),这个的正确性显然。

那么每次查询的答案就为\(\text{区间长度之和}-3\times\text{重复出现的数}\),求后面的就是3个bitset取&就可以了。

然后用莫队处理区间问题就没了。

但是要是开\(nm\)个bitset会寄,所以将询问分段处理即可。

点此查看代码
#include<bits/stdc++.h>
#include<bits/extc++.h>
// using namespace __gnu_pbds;
// using namespace __gnu_cxx;
using namespace std;
#define infile(x) freopen(x,"r",stdin)
#define outfile(x) freopen(x,"w",stdout)
#define errfile(x) freopen(x,"w",stderr)
#ifdef LOCAL
    FILE *InFile = infile("in.in"),*OutFile = outfile("out.out");
    // FILE *ErrFile=errfile("err.err");
#else
    FILE *Infile = stdin,*OutFile = stdout;
    //FILE *ErrFile = stderr;
#endif
using ll=long long;using ull=unsigned long long;
using db = double;using ldb = long double;
const int N = 1e5 + 10;
map<int,int> mp;
int n,a[N],m,ans[N],len,L[N],R[N],pos[N];
struct node{int l,r,id;}q[N];
int re[N];
bitset<N> pd[20010];
int cnt,que,num[N],ct[N];
inline void work(int m){
    int cnt = 0;
    memset(num,0,sizeof num);
    memset(ct,0,sizeof ct);
    for(int i = 1;i <= m; ++i){
        que++;
        cnt++;cin>>q[cnt].l>>q[cnt].r;q[cnt].id = i;num[i] += q[cnt].r - q[cnt].l + 1;
        cnt++;cin>>q[cnt].l>>q[cnt].r;q[cnt].id = i;num[i] += q[cnt].r - q[cnt].l + 1;
        cnt++;cin>>q[cnt].l>>q[cnt].r;q[cnt].id = i;num[i] += q[cnt].r - q[cnt].l + 1;
        re[i] = que;
        pd[i].set();
    }

    sort(q+1,q+1+cnt,[](node x,node y){return (pos[x.l] == pos[y.l]?(pos[x.l]&1?x.r<y.r:x.r>y.r):x.l<y.l);});

    
    bitset<N> vis;vis.reset();
    int left = 1,right = 0;
    auto add = [&](int x) -> void{
        vis.set(x - ct[x]);
        ct[x]++;
    };
    auto del = [&](int x) -> void{
        ct[x]--;
        vis.reset(x - ct[x]);
    };
    for(int i = 1;i <= cnt; ++i){
        int l = q[i].l,r = q[i].r;
        while(left > l) left--,add(a[left]);
        while(right < r) right++,add(a[right]);
        while(left < l) del(a[left]),left++;
        while(right > r) del(a[right]),right--;
        // cerr<<l<<' '<<r<<" :\n";
        // for(int j = 1;j <= 5; ++j) cerr<<vis[j];
        // cerr<<"\n\n";
        pd[q[i].id] &= vis;
    }
    for(int i = 1;i <= m; ++i){
        ans[re[i]] = num[i] - pd[i].count()*3;
    }
}
inline void solve(){
    cin>>n>>m;
    for(int i = 1;i <= n; ++i) cin>>a[i],mp[a[i]]++;

    len = sqrt(n);
    for(int i = 1;i <= len; ++i) L[i] = R[i - 1] + 1,R[i] = i * len;
    if(R[len] < n) len++,L[len] = R[len - 1] + 1,R[len] = n;
    for(int i = 1;i <= len; ++i) for(int j = L[i];j <= R[i]; ++j) pos[j] = i;

    int res = 0;
    for(auto &i:mp) i.second += res,res += i.second - res;
    for(int i = 1;i <= n; ++i) a[i] = mp[a[i]];
    // for(int i = 1;i <= n; ++i) cerr<<a[i]<<' ';
    // cerr<<'\n';

    int limit = 20000;
    res = m;
    while(true){
        if(m <= limit){work(m);break;}
        else work(limit),m -= limit;
    }
    for(int i = 1;i <= res; ++i) cout<<ans[i]<<'\n';
}
signed main(){
    cin.tie(nullptr)->sync_with_stdio(false);
    cout.tie(nullptr)->sync_with_stdio(false);
    solve();
}

upd : 速报,hangry已经肆意写FFT了,让我们一起膜拜他。

image

posted @ 2024-08-23 11:37  CuFeO4  阅读(20)  评论(3编辑  收藏  举报