【考试总结】2022-04-06

Fable

考虑冒泡排序的性质即如果某个数前面有元素大于之,那么一定在这轮这种元素数量恰好减少 1

那么可以计算出来经过 k 轮后的每个位置的数字前面有几个元素大于之,然后使用数据结构维护最小的前方没有元素大于之的数即可

这样子的数据结构需要支持区间减法,所以写了线段树

Code Display
const int N=200010;
int n,m,a[N],k,b[N];
struct Fenwick{
    int c[N];
    inline int query(int x){
        int res=0;
        for(;x<=m;x+=x&(-x)) res+=c[x];
        return res;
    }
    inline void insert(int x,int v){
        for(;x;x-=x&(-x)) c[x]++;
        return ;
    }
}T;
int lsh[N];
bool vis[N];
vector<int> inv[N];
struct Seg{
    int Mn[N<<2],tag[N<<2];
    inline void push_tag(int p,int v){Mn[p]+=v; tag[p]+=v;}
    inline void push_down(int p){
        if(tag[p]){
            push_tag(p<<1,tag[p]);
            push_tag(p<<1|1,tag[p]);
            tag[p]=0;
        } return ;
    }
    inline void push_up(int p){
        Mn[p]=min(Mn[p<<1],Mn[p<<1|1]);
        return ;
    }
    inline void build(int p,int l,int r){
        if(l==r){
            Mn[p]=inv[l].back();
            return ;
        }
        int mid=(l+r)>>1;
        build(p<<1,l,mid); build(p<<1|1,mid+1,r);
        return push_up(p);
    }
    inline void upd(int p,int l,int r,int st,int ed){ 
        if(st<=l&&r<=ed) return push_tag(p,-1);
        int mid=(l+r)>>1; push_down(p);
        upd(p<<1,l,mid,st,ed);
        if(ed>mid) upd(p<<1|1,mid+1,r,st,ed);
        return push_up(p);
    }
    inline int dfs(int p,int l,int r){
        if(l==r){
            inv[l].pop_back();
            Mn[p]=inv[l].size()?inv[l].back()+tag[p]:n+1;
            return l;
        }
        int mid=(l+r)>>1,res=0;
        push_down(p);
        if(Mn[p<<1]==0) res=dfs(p<<1,l,mid);
        else res=dfs(p<<1|1,mid+1,r);
        push_up(p);
        return res;
    }
}seg;
signed main(){
    freopen("fable.in","r",stdin); freopen("fable.out","w",stdout);
    n=read(); k=read();
    rep(i,1,n) a[i]=read(),lsh[i]=a[i];
    sort(lsh+1,lsh+n+1);
    m=unique(lsh+1,lsh+n+1)-lsh-1;
    rep(i,1,n){
        a[i]=lower_bound(lsh+1,lsh+m+1,a[i])-lsh;
        b[i]=T.query(a[i]+1);
        T.insert(a[i],1);
    }
    rep(i,1,n){
        b[i]=max(0ll,b[i]-k);
        inv[a[i]].emplace_back(b[i]);
    }
    rep(i,1,m){
        sort(inv[i].begin(),inv[i].end());
        reverse(inv[i].begin(),inv[i].end());
    }
    seg.build(1,1,m);
    rep(i,1,n){
        int v=seg.dfs(1,1,m);
        print(lsh[v]);
        if(v>1) seg.upd(1,1,m,1,v-1);
    }
    return 0;
}

Fiend

逆序对奇数减,偶数加,符合行列式定义,平凡做法就是直接求行列式 Ai,liri=1 的值

但是发现每行的 1 是连续的,可以对于每列找到所有这列有元的行中右端点最小的,并将其它的行 ljri+1

如果需要交换行那么需要将行列式的值取反

那么使用左偏树维护所有的左端点,要支持的操作是取出堆顶,合并两个堆

同时在左偏树的每个节点维护点对应的行编号以及逆映射,交换两行时就能维护正确的行号了

Code Display
const int N=1e5+10;
int n,nds;
int rt[N],ls[N],rs[N],dep[N],mp[N],id[N],val[N];
inline int merge(int x,int y){
    if(!x||!y) return x+y;
    if(val[x]>val[y]) swap(x,y);
    rs[x]=merge(rs[x],y);
    if(dep[rs[x]]>dep[ls[x]]) swap(ls[x],rs[x]);
    dep[x]=dep[rs[x]]+1;
    return x;
}
inline void pop(int &x){x=merge(ls[x],rs[x]);}
inline int New(int v,int lin){
    int p=++nds;
    mp[lin]=p; id[p]=lin;
    dep[p]=1;
    val[p]=v;
    return p;
}
signed main(){
    freopen("fiend.in","r",stdin); freopen("fiend.out","w",stdout);
    int T=read(); while(T--){
        n=read();
        for(int i=1;i<=n;++i){
            int l=read(),r=read();
            rt[l]=merge(rt[l],New(r,i));
        }
        int ans=1;
        for(int i=1;i<=n;++i){
            if(!rt[i]){ans=0; break;}
            if(id[rt[i]]!=i){
                id[mp[i]]=id[rt[i]];
                mp[id[rt[i]]]=mp[i];
                ans*=-1;
            }
            int v=val[rt[i]];
            pop(rt[i]);
            if(val[rt[i]]==v){ans=0; break;}
            if(v<n) rt[v+1]=merge(rt[v+1],rt[i]);
        }
        if(ans>0) puts("Y");
        else if(ans==0) puts("D");
        else puts("F");
        for(int i=1;i<=n;++i){
            dep[i]=ls[i]=rs[i]=val[i]=0;
            id[i]=mp[i]=0;
            rt[i]=0;
        }
        nds=0;
    }
    return 0;
}

Flair

使用互质数对 (a,b) 不能表示出来的最大数是 abab 推导不互质的数对可以发现:在 x>10000 后,每个菜品数量所消耗的钱数是每 GCD{c} 一循环的,同时在每个循环节中都是以 GCD10 方式递减

那么使用倍增 MTT 配合 fi,j=pfi1,j1+(1p)fi,j 计算 Gr=irmod GCDfn,i 后可以得到选择菜品数较多时候的答案

对于选择菜品数对应的浪费的钱数未进入循环节的情况,可以另做一个背包来算浪费数,而对应的选菜概率的计算可以使用 组合数等于下降幂除阶乘 的方式来解决

Code Display
const int N=10010;
int n,m,p,c[N];
const double pi=acos(-1);
struct node{
    long double x,y;
    node(){}
    node(long double xx,long double yy){x=xx; y=yy; return ;}
    node operator +(const node &a)const{return node(x+a.x,y+a.y);}
    node operator -(const node &a)const{return node(x-a.x,y-a.y);}
    node operator *(const node &a)const{return node(x*a.x-y*a.y,x*a.y+y*a.x);}
    inline node conj(){return node(x,-y);}
    inline void clear(){x=y=0;}
}A0[N<<2],B0[N<<2],A1[N<<2],B1[N<<2],W[N<<2],F[N<<2],G[N<<2],H[N<<2];
int r[N<<2];
inline void FFT(node *f,int lim,int opt){
	rep(i,0,lim-1){
        if(i<r[i]) swap(f[i],f[r[i]]);
    }
    for(int p=2;p<=lim;p<<=1){
	    int len=p>>1; node tt;
        W[0]=node(1,0); 
        rep(i,1,len-1) W[i]=node(cos(pi/len*i),opt*sin(pi/len*i));
	    for(int k=0;k<lim;k+=p){
            for(int l=k;l<k+len;++l){
                tt=f[l+len]*W[l-k];
                f[len+l]=f[l]-tt,f[l]=f[l]+tt;
            }
        }
    }
    if(opt==-1) rep(i,0,lim-1) f[i].x/=lim;
}
int poly[N],bas[N],g;
inline void MTT(int *a,int *b,int *res,int n,int m){
    int lim=1; while(lim<=(n+m)) lim<<=1;
    rep(i,0,lim-1){
        A0[i].x=A0[i].y=0;
        B1[i].x=B1[i].y=0;
        B0[i].x=B0[i].y=0;
        A1[i].x=A1[i].y=0;
        H[i].x=H[i].y=0;
        G[i].x=G[i].y=0;
        F[i].x=F[i].y=0;
        r[i]=r[i>>1]>>1|((i&1)?(lim>>1):0);
    }
    for(int i=0;i<n;++i) A0[i].x=a[i]>>15,A0[i].y=a[i]&32767; FFT(A0,lim,1);
    for(int i=0;i<m;++i) B0[i].x=b[i]>>15,B0[i].y=b[i]&32767; FFT(B0,lim,1);
    for(int i=0;i<lim;++i) A1[i]=A0[i?lim-i:0].conj(),B1[i]=B0[i?lim-i:0].conj();
    node p,q;
    for(int i=0;i<lim;++i){
        p=A0[i],q=A1[i];
        A0[i]=(p+q)*node(0.5,0),A1[i]=(q-p)*node(0,0.5); 
        p=B0[i],q=B1[i]; 
        B0[i]=(p+q)*node(0.5,0),B1[i]=(q-p)*node(0,0.5);
        F[i]=A0[i]*B0[i]*node(1,0); 
        G[i]=A0[i]*B1[i]*node(1,0)+A1[i]*B0[i]*node(1,0);
        H[i]=A1[i]*B1[i]*node(1,0);
    }
    FFT(F,lim,-1); FFT(G,lim,-1); FFT(H,lim,-1); 
    int up=min(g,n+m-1);
    rep(i,0,up-1) res[i]=0;
    for(int i=0;i<lim;++i) res[i%g]+=(int)(F[i].x+0.5)%mod*((1<<30)%mod)%mod+(int)(G[i].x+0.5)%mod*(1<<15)%mod+(int)(H[i].x+0.5)%mod; 
    for(int i=0;i<up;++i) res[i]=(res[i]%mod+mod)%mod;
}
int ifac[N];
signed main(){
    freopen("flair.in","r",stdin); freopen("flair.out","w",stdout);
    n=1e4; ifac[0]=1;
    for(int i=1;i<=n;++i) ifac[i]=mul(ifac[i-1],ksm(i,mod-2));
    int T=read(); while(T--){
        n=read(); m=read(); p=read();
        g=-1;
        rep(i,1,m) g=g==-1?c[i]=read():__gcd(g,c[i]=read());
        sort(c+1,c+m+1);
        vector<int> dp(20010,1000100);
        dp[0]=0;
        int up=2e4;
        for(int i=1;i<=m;++i){
            for(int j=0;j+c[i]<=up;++j) if(dp[j]==0) dp[j+c[i]]=0;
        }
        for(int i=up-1;i>=0;--i) ckmin(dp[i],dp[i+1]+1);
        rep(i,0,g-1) poly[i]=bas[i]=0;
        poly[0]=1;
        bas[1]=p; bas[0]=100-p;
        int y=n,lenb=2,lenp=1;
        while(y){
            if(y&1){
                MTT(poly,bas,poly,lenp,lenb);
                lenp=min(g,lenp+lenb-1);
            }
            MTT(bas,bas,bas,lenb,lenb);
            lenb=min(g,lenb*2-1);
            y>>=1;
        }
        int ans=0;
        int dfac=1,pwp=1,curpw=ksm(100-p,n),Inv=ksm(100-p,mod-2);
        for(int i=0;i<=min(n,10000ll);++i){
            int contr=mul(mul(pwp,curpw),mul(dfac,ifac[i]));
            ckadd(ans,mul(dp[i],contr));
            ckdel(poly[i%g],contr);
            ckmul(dfac,n-i);
            ckmul(pwp,p);
            ckmul(curpw,Inv);
        }
        for(int i=1;i<g;++i) ckadd(ans,mul(poly[i],g-i));
        print(ans);
    }
    return 0;
}

posted @   没学完四大礼包不改名  阅读(101)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 单元测试从入门到精通
· 上周热点回顾(3.3-3.9)
· winform 绘制太阳,地球,月球 运作规律
点击右上角即可分享
微信分享提示