警示后人&&提问

P4168 [Violet] 蒲公英

l55584 @ 2022-08-29 14:56:34

看着蓝书写的(就是李煜东的《算法竞赛进阶指南》)

将块数T和长度len设置为书上写的

T=sqrt(n*(log(n)/log(2)));
len=n/T;

WA0分

设置为

T=sqrt(n);
len=n/T;

A了

如果可能是我代码的问题,在此附上代码

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <vector>
#include <string.h>
#include <cmath>
#define rep(a,b,c) for(register int a=b;a<=c;++a)
using namespace std;
int T,n,m;
typedef long long ll;
const int N=40001;
int len;
//快读------------------------------------------ 
inline char gc() {
    static char BB[1000000],*S=BB,*T=BB;
    return S==T&&(T=(S=BB)+fread(BB,1,1000000,stdin),S==T)?EOF:*S++;
}
inline int read() {
    int x=0,f=1;
    char ch=gc();
    while(ch<'0'||ch>'9') {
        if(ch=='-') f=-1;
        ch=gc();
    }
    while(ch>='0'&&ch<='9') {
        x=x*10+ch-48;
        ch=gc();
    }
    return x*f;
}
//---------------------------------------------- 
vector<int> dic;
vector<int> d[N];
int p[1001][1001];
int a[N];
void init() {
    n=read();
    m=read();
    //T=sqrt(n*(log(n)/log(2)));
    //len=n/T;
    T=sqrt(n);
    len=n/T;
    rep(i,1,n) {
        a[i]=read();
        dic.push_back(a[i]);
    }
    //-------------------------------------------------离散化
    sort(dic.begin(),dic.end());
    int tot=unique(dic.begin(),dic.end())-dic.begin();
    rep(i,1,n) a[i]=lower_bound(dic.begin(),dic.begin()+tot,a[i])-dic.begin()+1;
    //-------------------------------------------------------
    rep(i,1,n) d[a[i]].push_back(i);//为num()查询构建d数组,记录每一种蒲公英的出现位置 
    int v[N];//桶 
    //-----------------------------------预处理块间众数
    rep(i,1,T) {
        memset(v,0,sizeof v);
        int mmax[2]= {0,0};
        rep(j,i,T) {
            int r=j*len,l=r-len+1;
            rep(k,l,r) {
                v[a[k]]++;
                if(v[a[k]]>mmax[0]||(v[a[k]]==mmax[0]&&a[k]<mmax[1])) {//取编号最小 
                    mmax[0]=v[a[k]],mmax[1]=a[k];
                }
            }
            p[i][j]=mmax[1];//p[i][j]记录第i块到第j块(左闭右闭)众数 
        }
    }
    //--------------------------------------------------
}
inline int num(int k,int l,int r) {//求区间k类蒲公英的数量 
    int up=upper_bound(d[k].begin(),d[k].end(),r)-d[k].begin()-1;
    int down=lower_bound(d[k].begin(),d[k].end(),l)-d[k].begin();
    return up-down+1;
}
int query(int l,int r) {
    int L=ceil(l*1.0/len)+1,R=ceil(r*1.0/len)-1;//区间内可用块的区间
    int now=num(p[L][R],l,r),ret=p[L][R];
    rep(i,l,(L-1)*len) {//trival左边 
        int cnt=num(a[i],l,r);
        if(cnt>now||(cnt==now&&a[i]<ret)) {//注意取编号最小 
            now=cnt;
            ret=a[i];
        }
    }
    rep(i,(R*len)+1,r) {//trival右边 
        int cnt=num(a[i],l,r);
        if(cnt>now||(cnt==now&&a[i]<ret)) {//注意取编号最小 
            now=cnt;
            ret=a[i];
        }
    }
    return ret;
}
int main() {
    //freopen("in.in","r",stdin);
    //freopen("P4168_1.in","r",stdin);
    //freopen("out.out","w",stdout);
    init();
    ll l,r,lasans=0;
    rep(i,1,T) {
//      rep(j,i,T) cout<<(i-1)*len+1<<" "<<j*len<<" "<<p[i][j]<<"\n";
    }
    rep(k,1,m) {
        l=read(),r=read();
        l=(l+lasans-1)%n+1;
        r=(r+lasans-1)%n+1;
        if(l>r) swap(l,r);
        lasans=dic[query(l,r)-1];//还原,注意-1 
        cout<<lasans<<endl;
    }
    return 0;
}

by yinhee @ 2022-08-29 15:15:50

您这样块数就是 \sqrt{n\log n}


by l55584 @ 2022-08-29 15:24:50

@yinhee 这个算法复杂度为O(NT+MN/T*logN)

块数应该设为sqrt(N*logN)

我是这么想的,书上也是这么写的。

或许是我理解错了?可以再详细说一下吗


by Texas_the_Omertosa @ 2022-08-29 15:25:23

@l55584 我经常照蓝书写,经常 WA,所以我现在经常就是看蓝书学理论,代码不知道怎么写就看题解。


by Texas_the_Omertosa @ 2022-08-29 15:26:37

@l55584 块的长度一般设为 \sqrt N 吧?


by l55584 @ 2022-08-29 15:27:01

@olkieler 谢谢建议,帮大忙了


by l55584 @ 2022-08-29 15:29:25

@olkieler 这样时间复杂度会不会出现错误的情况?

好像也不一定……比如莫队用sqrtN会被卡


by Texas_the_Omertosa @ 2022-08-29 15:32:58

@l55584 我刚学分块,莫队还没学(我连那是什么都不知道),我觉得分块没有线段树好写,就只学了理论。


|