ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

【考试总结】2022-05-01

2022-05-02 21:04:24  阅读:152  来源: 互联网

标签:opt 01 return 05 int lim rson 2022 push


a

观察能构成合法区间的条件就是奇数成对,同时每对奇数之间没有 \(0\)

使用一类线段树维护历史信息的手段来解决本题,为了规避麻烦的标记下放,可以将每个 “偶数个奇数” 的出现区间左右端点刻画出来并使用 \(r-l+1\) 来计算

同时在每个节点要支持 \(01\) 翻转的部分维护 “对未翻转时奇数/偶数” 的权值增加量方便下方

Code Display
const int N=5e5+10;
int n,Q,a[N],his[N],ans[N];
vector<pair<int,int> > qu[N];
bool ban[N],od[N];
#define ls p<<1
#define rs p<<1|1
#define lson p<<1,l,mid
#define rson p<<1|1,mid+1,r
bool rev[N<<2];
int num[N<<2][2],delt[N<<2][2],sum[N<<2];
inline void push_up(int p){
    sum[p]=sum[ls]+sum[rs];
    num[p][0]=num[ls][0]+num[rs][0];
    num[p][1]=num[ls][1]+num[rs][1];
    return ;
}
inline void push_rev(int p){rev[p]^=1; swap(num[p][0],num[p][1]);}
inline void push_add(int p,int v,int fl){
    delt[p][rev[p]^fl]+=v;
    sum[p]+=num[p][fl]*v;
    return ;
}
inline void push_down(int p){
    for(int i=0;i<2;++i) if(delt[p][i]){
        push_add(ls,delt[p][i],i);
        push_add(rs,delt[p][i],i);
        delt[p][i]=0;
    }
    if(rev[p]){
        push_rev(ls); push_rev(rs);
        rev[p]=0;
    }
    return ;
}
inline void upd(int R,int v,int fl,int p=1,int l=1,int r=n){
    if(r<=R) return push_add(p,v,fl),void();
    int mid=(l+r)>>1; push_down(p);
    if(R>mid) upd(R,v,fl,rson);
    upd(R,v,fl,lson);
    return push_up(p);
}
inline int query(int st,int ed,int p=1,int l=1,int r=n){
    if(st<=l&&r<=ed) return sum[p];
    int mid=(l+r)>>1,res=0; push_down(p);
    if(ed>mid) res+=query(st,ed,rson);
    if(st<=mid) res+=query(st,ed,lson);
    return res;
}
inline void dfs(int p,int l,int r){
    if(!num[p][1]) return ;
    if(l==r){
        delt[p][0]=delt[p][1]=0;
        num[p][0]=num[p][1]=0;
        return ;
    }
    int mid=(l+r)>>1; push_down(p);
    dfs(lson); dfs(rson);
    return push_up(p);
}
inline void Reverse(int R,int p=1,int l=1,int r=n){
    if(r<=R) return push_rev(p);
    int mid=(l+r)>>1; push_down(p);
    if(R>mid) Reverse(R,rson);
    Reverse(R,lson);
    return push_up(p);
}
inline void build(int p,int l,int r){
    if(l==r){
        num[p][0]=1;
        sum[p]=-l;
        return ;
    }
    int mid=(l+r)>>1;
    build(lson); build(rson);
    return push_up(p);
}
inline void output(int p,int l,int r){
    if(l==r) return cout<<sum[p]<<" ",void();
    int mid=(l+r)>>1; push_down(p);
    output(lson); output(rson);
    return ;
}
#undef ls
#undef rs
#undef lson
#undef rson
signed main(){
    freopen("a.in","r",stdin); freopen("a.out","w",stdout);
    n=read(); Q=read();
    for(int i=1;i<=n;++i) a[i]=read();
    for(int i=1;i<=Q;++i){
        int l=read(),r=read();
        qu[r].emplace_back(l,i);
    }
    build(1,1,n);
    for(int i=1;i<=n;++i){
        if(!a[i]&&i) dfs(1,1,n);
        if(a[i]&1){
            upd(i,i,0);
            upd(i,-i,1);
            Reverse(i);
        }
        for(auto t:qu[i]){
            upd(i,i+1,0);
            ans[t.sec]+=query(t.fir,i);
            upd(i,-1-i,0);
        }
    }
    rep(i,1,Q) print(ans[i]);
    return 0;
}

b

对于 \(Q\) 个属于 \([1,m]\) 的 随机区间,将左端点排序后,对应的右端点最长上升子序列长度是 \(\Theta(\sqrt Q)\) 级别的

那么将所有询问分成 \(\dfrac{Q}{\sqrt Q}\) 组,每组中询问形成嵌套关系,使用势能线段树暴力扩展即可

但是据说会被卡常,所以代码写了个能过的暴力

Code Display
const int N=15010;
int n,m,Q,sum[N<<2];
struct operation{
    int l,r,v,id;
}opt[N<<1];
inline void upd(int p,int l,int r,int st,int ed){
    if(sum[p]==r-l+1) return ;
    if(st<=l&&r<=ed) return sum[p]=r-l+1,void();
    int mid=(l+r)>>1;
    if(st<=mid) upd(p<<1,l,mid,st,ed);
    if(ed>mid) upd(p<<1|1,mid+1,r,st,ed);
    sum[p]=sum[p<<1]+sum[p<<1|1];
    return ;
}
inline void build(int p,int l,int r){
    sum[p]=0; if(l==r) return ;
    int mid=(l+r)>>1;
    build(p<<1,l,mid); build(p<<1|1,mid+1,r);
}
signed main(){
    freopen("b.in","r",stdin); freopen("b.out","w",stdout);
    n=read(); m=read(); Q=read();
    for(int i=1;i<=n;++i) opt[i]={i,i,read(),0};
    int cnt=n;
    for(int i=1;i<=m;++i){
        int l=read(),r=read(),v=read();
        opt[++cnt]={l,r,v,i};
    }
    sort(opt+1,opt+cnt+1,[&](const operation A,const operation B){return A.v<B.v;});
    while(Q--){
        int l1=read(),r1=read(),l2=read(),r2=read();
        int lst=0;
        int ans=0;
        int siz=r2-l2+1;
        build(1,1,siz);
        for(int i=1;i<=cnt;++i){
            if(opt[i].id&&(opt[i].id<l1||opt[i].id>r1)) continue;
            if(opt[i].r<l2||opt[i].l>r2) continue;
            upd(1,1,siz,max(opt[i].l,l2)-l2+1,min(opt[i].r,r2)-l2+1);
            ans+=(sum[1]-lst)*opt[i].v;
            if(sum[1]==siz) break;   
            lst=sum[1];
        }
        print(ans);
    }
    return 0;
}

c

发现本质上要完成的工作是对 \(n\times K\) 个点值做背包,但是每个元素的值域只有 \(\pm1\) ,也就是在 \(n\) 个数字里面找到 \(m\) 个并将它们乘起来再对所有选择方案的权值求和

设有 \(x\) 个元素贡献 \(1\) 而 \(n-x\) 个元素贡献 \(-1\),对于所有 \(x\) 求出来 \([z^m](1-z)^{n-x}(1+z)^{x}\)

而这通过分配幂次变成 \(\displaystyle \sum_{i=0}^m(-1)^{m-i}\binom{n-x}{m-i}\binom{x}i\)

再拆成阶乘逆元可以通过一次卷积进行计算

在对 \(a_i\) 形成的桶做 \(\rm FWT\) 后得到的点值解方程可以得到上面所写的 \(x\),那么直接代换成背包后的结果再做逆变换得到答案

Code Display
const int N=2e5+10;
int n,m,K;
inline void FWT(vector<int> &f,int lim){
    for(int p=2;p<=lim;p<<=1){
        int len=p>>1;
        for(int k=0;k<lim;k+=p) for(int l=k;l<k+len;++l){
            int x=f[l],y=f[l+len];
            f[l+len]=x-y;
            f[l]=x+y;
        }
    }
    return ;
}
inline void iFWT(vector<int> &f,int lim){
    for(int p=2;p<=lim;p<<=1){
        int len=p>>1;
        for(int k=0;k<lim;k+=p) for(int l=k;l<k+len;++l){
            int x=f[l],y=f[l+len];
            f[l+len]=del(x,y);
            f[l]=add(x,y);
        }
    }
    for(int tmp=ksm(lim,mod-2),i=0;i<lim;++i) ckmul(f[i],tmp);
    return ;
}
int W[1<<20],r[1<<20];
inline void NTT(vector<int> &f,int lim,int opt){
    f.resize(lim);
    for(int i=0;i<lim;++i){
        r[i]=r[i>>1]>>1|((i&1)?(lim>>1):0);
        if(i<r[i]) swap(f[i],f[r[i]]);
    }
    for(int p=2;p<=lim;p<<=1){
        int len=p>>1; W[0]=1; 
        W[1]=ksm(3,(mod-1)/p);
        if(opt==-1) W[1]=ksm(W[1],mod-2);
        for(int j=2;j<len;++j) W[j]=mul(W[j-1],W[1]);
        for(int k=0;k<lim;k+=p){
            for(int l=k;l<k+len;++l){
                int tt=mul(f[l+len],W[l-k]);
                f[l+len]=del(f[l],tt);
                ckadd(f[l],tt);
            }
        }        
    }
    if(opt==-1) for(int tmp=ksm(lim,mod-2),i=0;i<lim;++i) ckmul(f[i],tmp);
}
int ifac[N],fac[N];
signed main(){
    freopen("c.in","r",stdin); freopen("c.out","w",stdout);
    n=read(); m=read(); K=read();
    vector<int> f,g,a(K);
    for(int i=1;i<=n;++i) a[read()]++;
    if(m==1){
        rep(i,0,K-1) print(a[i]);
        putchar('\n');
        exit(0);
    }
    fac[0]=1;
    for(int i=1;i<=n;++i) fac[i]=mul(fac[i-1],i);
    ifac[n]=ksm(fac[n],mod-2);
    for(int i=n;i>=1;--i) ifac[i-1]=mul(ifac[i],i);
    
    FWT(a,K);
    int lim=1;
    while(lim<=n) lim<<=1;
    f.resize(m+1);
    g.resize(n-m+1);
    for(int i=0;i<=m;++i){
        f[i]=mul(ifac[i],ifac[m-i]);
        if((m-i)&1) f[i]=del(0,f[i]);
    }
    for(int i=0;i<=n-m;++i) g[i]=mul(ifac[i],ifac[n-m-i]);
    NTT(f,lim,1); NTT(g,lim,1);
    for(int i=0;i<lim;++i) ckmul(f[i],g[i]);
    NTT(f,lim,-1);
    for(int i=0;i<=n;++i) ckmul(f[i],mul(fac[i],fac[n-i]));
    for(int i=0;i<K;++i) a[i]=f[(n-a[i])/2];
    iFWT(a,K);
    for(int i=0;i<K;++i) print(a[i]); putchar('\n');
    return 0;
}

标签:opt,01,return,05,int,lim,rson,2022,push
来源: https://www.cnblogs.com/yspm/p/16216788.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有