冲刺清北营 7

ZROI 2022 省选十连测 Day7。我在验 23 的 Day7 的时候突然考了 22 的 Day7。

今天 T3 充分的说明了我脑子有问题的事实。属于是推式子推魔怔了忘了这是个直接 FWT 就能出的东西。可能以后早上应该吃饱一点来应对数据结构后边跟着数学的情况。

为啥 T1 都写这么快,就我调到十一点。

a

首先不考虑 \(0\)。那么一个序列合法则所有元素和为偶数。那么扫描线变成 \(01\) 串支持区间异或,区间历史和。这个可以线段树每个节点多维护个有几个历史版本为 \(0\),几个历史版本为 \(1\) 的标记来做到(然而这玩意写死我了直接导致 T3 没切)。我是用所有的情况减掉不合法的,也就是异或和为 \(1\) 的。

然后考虑加上 \(0\) 的情况。此时 \(0\) 把整个序列分成若干段。称整段为夹在两个 \(0\) 之间的段。仍然线段树扫描线,设当前到 \(i\),把整个序列分成三段 \([1,pre),[pre,last),[last,i]\)。第一段的结尾是最后一个不合法的整段,即这一段不会再对答案造成贡献,是被删除的段。第二段的开头 \(pre\) 是最后一个不合法的整段的结尾 \(0\) 处,代表这些整段是合法的,可能会造成贡献。第三个整段是当前扫到的整段。每次扫到一个数,第一段历史和区间 \(+1\) 表示无贡献,然后将第三个整段异或 \(a_i \wedge 1\),这样就可以通过 \(last\) 位置的 \(0/1\) 判断第二段是否有贡献,做区间历史和 \(+1\) 或是区间异或 \(0\)

#include <cstdio>
#include <algorithm>
#include <iostream>
#include <cstring>
#include <vector>
#define int long long
using namespace std;
#define lson rt<<1
#define rson rt<<1|1
int n,m,a[500010],ans[500010];
int read(){
    int x=0;char ch=getchar();
    while(!isdigit(ch))ch=getchar();
    while(isdigit(ch))x=10*x+ch-'0',ch=getchar();
    return x;
}
struct node{
    int sum,sum2,cnt[2],lz,len,lz2;
}tree[2000010];
void build(int rt,int l,int r){
    tree[rt].len=r-l+1;
    if(l==r)return;
    int mid=(l+r)>>1;
    build(lson,l,mid);build(rson,mid+1,r);
}
void pushup(int rt){
    tree[rt].sum=tree[lson].sum+tree[rson].sum;
    tree[rt].sum2=tree[lson].sum2+tree[rson].sum2;
}
void pushtag(int rt,int sum,int cnt[],int lz){
    tree[rt].sum2+=tree[rt].sum*cnt[0]+(tree[rt].len-tree[rt].sum)*cnt[1];
    tree[rt].cnt[0]+=cnt[tree[rt].lz];tree[rt].cnt[1]+=cnt[tree[rt].lz^1];
    if(lz)tree[rt].sum=tree[rt].len-tree[rt].sum,tree[rt].lz^=lz;
}
void pushdown(int rt){
    pushtag(lson,tree[rt].sum,tree[rt].cnt,tree[rt].lz);
    pushtag(rson,tree[rt].sum,tree[rt].cnt,tree[rt].lz);
    if(tree[rt].lz2){
        tree[lson].sum2+=tree[lson].len*tree[rt].lz2;tree[rson].sum2+=tree[rson].len*tree[rt].lz2;
        tree[lson].lz2+=tree[rt].lz2;tree[rson].lz2+=tree[rt].lz2;
        tree[rt].lz2=0;
    }
    tree[rt].cnt[0]=tree[rt].cnt[1]=tree[rt].lz=0;
}
void update(int rt,int L,int R,int l,int r,int val){
    if(l>r)return;
    if(l<=L&&R<=r){
        if(val){
            tree[rt].lz^=1;tree[rt].sum=tree[rt].len-tree[rt].sum;
        }
        tree[rt].sum2+=tree[rt].sum;tree[rt].cnt[tree[rt].lz]++;
        return;
    }
    pushdown(rt);
    int mid=(L+R)>>1;
    if(l<=mid)update(lson,L,mid,l,r,val);
    if(mid<r)update(rson,mid+1,R,l,r,val);
    pushup(rt);
}
void update(int rt,int L,int R,int l,int r){
    if(l>r)return;
    if(l<=L&&R<=r){
        tree[rt].sum2+=tree[rt].len;tree[rt].lz2++;
        return;
    }
    pushdown(rt);
    int mid=(L+R)>>1;
    if(l<=mid)update(lson,L,mid,l,r);
    if(mid<r)update(rson,mid+1,R,l,r);
    pushup(rt);
}
int query(int rt,int L,int R,int l,int r){
    if(l<=L&&R<=r)return tree[rt].sum2;
    pushdown(rt);
    int mid=(L+R)>>1,val=0;
    if(l<=mid)val+=query(lson,L,mid,l,r);
    if(mid<r)val+=query(rson,mid+1,R,l,r);
    return val;
}
int query(int rt,int L,int R,int pos){
    if(L==R)return tree[rt].sum;
    pushdown(rt);
    int mid=(L+R)>>1;
    if(pos<=mid)return query(lson,L,mid,pos);
    else return query(rson,mid+1,R,pos);
}
vector<pair<int,int> >q[500010];
int C(int n){
    return n*(n+1)>>1;
}
signed main(){
    n=read();m=read();
    build(1,1,n);
    for(int i=1;i<=n;i++)a[i]=read();
    for(int i=1;i<=m;i++){
        int l=read(),r=read();
        q[r].push_back(make_pair(l,i));
    }
    int pre=1,last=1,cnt=0;
    for(int i=1;i<=n;i++){
        update(1,1,n,1,pre-1);
        update(1,1,n,last,i,a[i]&1);
        if(query(1,1,n,last)==1)update(1,1,n,pre,last-1);
        else update(1,1,n,pre,last-1,0);
        for(pair<int,int>p:q[i])ans[p.second]=C(i-p.first+1)-query(1,1,n,p.first,i);
        if(a[i]==0){
            if(query(1,1,n,last)==1)pre=last;
            last=i;
        }
    }
    for(int i=1;i<=m;i++)printf("%lld\n",ans[i]);
    return 0;
}

b

赛时一车假的暴力艹掉了这题。然而我调完 T1 已经十一点了 T2 T3 还没想就敲了两个暴力,主要是身体条件不支持。

结论是随机区间按左端点排序后右端点的 LIS 期望是 \(O(\sqrt q)\) 的。那么询问可以分成 \(O(\sqrt q)\) 组,每组修改集合是嵌套关系,上线段树就行了。

#include <algorithm>
#include <cstring>
#include <cstdio>
#include <iostream>
#include <queue>
#define lson rt<<1
#define rson rt<<1|1
using namespace std;
int n,m,q,a[15010];
struct node{
    int mx,se,cnt,lz;
    long long sum;
}tree[60010];
void pushup(int rt){
    tree[rt].sum=tree[lson].sum+tree[rson].sum;
    if(tree[lson].mx>tree[rson].mx){
        tree[rt].mx=tree[lson].mx;
        tree[rt].cnt=tree[lson].cnt;
        tree[rt].se=max(tree[lson].se,tree[rson].mx);
    }
    else if(tree[lson].mx<tree[rson].mx){
        tree[rt].mx=tree[rson].mx;
        tree[rt].cnt=tree[rson].cnt;
        tree[rt].se=max(tree[lson].mx,tree[rson].se);
    }
    else{
        tree[rt].mx=tree[lson].mx;
        tree[rt].cnt=tree[lson].cnt+tree[rson].cnt;
        tree[rt].se=max(tree[lson].se,tree[rson].se);
    }
}
void pushtag(int rt,int val){
    if(val<tree[rt].mx){
        tree[rt].sum-=1ll*(tree[rt].mx-val)*tree[rt].cnt;
        tree[rt].mx=tree[rt].lz=val;
    }
}
void pushdown(int rt){
    if(~tree[rt].lz){
        pushtag(lson,tree[rt].lz);
        pushtag(rson,tree[rt].lz);
        tree[rt].lz=-1;
    }
}
void build(int rt,int l,int r){
    tree[rt].lz=-1;
    if(l==r){
        tree[rt].mx=tree[rt].sum=a[l];
        tree[rt].cnt=1;tree[rt].se=0;return;
    }
    int mid=(l+r)>>1;
    build(lson,l,mid);build(rson,mid+1,r);
    pushup(rt);
}
void update(int rt,int L,int R,int l,int r,int val){
    if(val>=tree[rt].mx)return;
    if(l<=L&&R<=r&&tree[rt].se<val){
        pushtag(rt,val);return;
    }
    pushdown(rt);
    int mid=(L+R)>>1;
    if(l<=mid)update(lson,L,mid,l,r,val);
    if(mid<r)update(rson,mid+1,R,l,r,val);
    pushup(rt);
}
long long query(int rt,int L,int R,int l,int r){
    if(l<=L&&R<=r)return tree[rt].sum;
    pushdown(rt);
    int mid=(L+R)>>1;long long val=0;
    if(l<=mid)val+=query(lson,L,mid,l,r);
    if(mid<r)val+=query(rson,mid+1,R,l,r);
    return val;
}
struct Sol{
    int l,r,x;
}s[15010];
struct Ques{
    int l1,r1,l2,r2,id;
    bool operator<(const Ques&s)const{
        return l1<s.l1;
    }
}ques[100010];
vector<Ques>v[100010];
long long ans[100010];
int main(){
    scanf("%d%d%d",&n,&m,&q);
    for(int i=1;i<=n;i++)scanf("%d",&a[i]);
    for(int i=1;i<=m;i++)scanf("%d%d%d",&s[i].l,&s[i].r,&s[i].x);
    for(int i=1;i<=q;i++)scanf("%d%d%d%d",&ques[i].l1,&ques[i].r1,&ques[i].l2,&ques[i].r2),ques[i].id=i;
    sort(ques+1,ques+q+1);
    for(int i=1;i<=q;i++){
        for(int j=1;j<=q;j++){
            if(v[j].empty()||ques[i].r1<=v[j].back().r1){
                v[j].push_back(ques[i]);break;
            }
        }
    }
    for(int i=1;i<=q;i++){
        if(v[i].empty())continue;
        build(1,1,n);
        for(int j=v[i].back().l1;j<=v[i].back().r1;j++)update(1,1,n,s[j].l,s[j].r,s[j].x);
        int l=v[i].back().l1,r=v[i].back().r1;
        for(int j=v[i].size()-1;j>=0;j--){
            while(l>v[i][j].l1){
                l--;update(1,1,n,s[l].l,s[l].r,s[l].x);
            }
            while(r<v[i][j].r1){
                r++;update(1,1,n,s[r].l,s[r].r,s[r].x);
            }
            ans[v[i][j].id]=query(1,1,n,v[i][j].l2,v[i][j].r2);
        }
    }
    for(int i=1;i<=q;i++)printf("%lld\n",ans[i]);
    return 0;
}

c

大水题没切怎么办大水题没切怎么办大水题没切怎么办大水题没切怎么办大水题没切怎么办大水题没切怎么办大水题没切怎么办大水题没切怎么办大水题没切怎么办大水题没切怎么办大水题没切怎么办大水题没切怎么办大水题没切怎么办大水题没切怎么办大水题没切怎么办大水题没切怎么办大水题没切怎么办大水题没切怎么办大水题没切怎么办大水题没切怎么办大水题没切怎么办大水题没切怎么办大水题没切怎么办大水题没切怎么办大水题没切怎么办大水题没切怎么办

首先明确答案是 \([x^m]\prod_{i=1}^n(1+xy^{a_i})\)\([0,k)\) 项系数。其中 \(x\) 是加法卷积,\(y\) 是异或卷积。

套路拆 FWT:\([y^w]FWT(1+xy^{a_i})=\begin{cases}1+x,w\otimes a_i=0\\1-x,w\otimes a_i=1\end{cases}\)。那么得到

\[[y^w]FWT\left(\prod_{i=1}^n(1+xy^{a_i})\right)=(1-x)^{cnt_w}(1+x)^{n-cnt_w} \]

其中 \(cnt_w=\sum_{i=1}^nw\otimes a_i\)

先考虑算 \(cnt_w\)。发现如果对原来的 \(a_i\) 开个桶 FWT 一下那么第 \(w\) 项就是 \(\sum_{i=1}^n[w\oplus a_i=0]-\sum_{i=1}^n[w\oplus a_i=1]\)。然后这两个加起来是 \(n\),解方程就得到 \(cnt_w\)。和黎明前的巧克力那题是一样的。

然后对每个 \(w\)\([x^m](1-x)^w(1+x)^{n-w}\)。暴力拆一下得到它是

\[\begin{aligned} &\sum_{i=0}^m(-1)^i\binom wi\binom{n-w}{m-i}\\ =&w!(n-w)!\sum_{i=0}^m\frac{(-1)^i}{i!(w-i)!}\frac 1{(m-i)!(n-m-(w-i))!} \end{aligned} \]

NTT 算一遍就行了。题解说分治 NTT 好像是可以做的,没细想。

我好菜!

#include <cstdio>
#include <algorithm>
#include <iostream>
#include <cstring>
#include <vector>
#include <set>
using namespace std;
const int mod=998244353;
typedef vector<int> poly;
int n,m,k,b[1<<17];
int wl,w[300010],jc[300010],inv[300010];
void get(int n){wl=1;while(wl<n)wl<<=1;}
#define add(x,y) (x+y>=mod?x+y-mod:x+y)
#define sub(x,y) (x<y?x-y+mod:x-y)
int qpow(int a,int b){
    int ans=1;
    while(b){
        if(b&1)ans=1ll*ans*a%mod;
        a=1ll*a*a%mod;
        b>>=1;
    }
    return ans;
}
void init(int n){
    int t=1;
    while((1<<t)<n)t++;
    t=min(t-1,21);
    w[0]=1;w[1<<t]=qpow(31,1<<21-t);jc[0]=inv[0]=inv[1]=1;
    for(int i=t;i>=1;i--)w[1<<i-1]=1ll*w[1<<i]*w[1<<i]%mod;
    for(int i=1;i<(1<<t);i++)w[i]=1ll*w[i&i-1]*w[i&-i]%mod;
    for(int i=2;i<=n;i++)inv[i]=1ll*(mod-mod/i)*inv[mod%i]%mod;
    for(int i=1;i<=n;i++)jc[i]=1ll*jc[i-1]*i%mod,inv[i]=1ll*inv[i-1]*inv[i]%mod;
}
inline void DIF(poly &a){
    int n=a.size();
    for(int mid=n>>1;mid;mid>>=1){
        for(int i=0,k=0;i<n;i+=mid<<1,k++){
            for(int j=0;j<mid;j++){
                int x=a[i+j],y=1ll*a[i+j+mid]*w[k]%mod;
                a[i+j]=add(x,y);a[i+j+mid]=sub(x,y);
            }
        }
    }
}
inline void DIT(poly &a){
    int n=a.size();
    for(int mid=1;mid<n;mid<<=1){
        for(int i=0,k=0;i<n;i+=mid<<1,k++){
            for(int j=0;j<mid;j++){
                int x=a[i+j],y=a[i+j+mid];
                a[i+j]=add(x,y);a[i+j+mid]=1ll*sub(x,y)*w[k]%mod;
            }
        }
    }
    for(int i=1;i<=(n-1)>>1;i++)swap(a[i],a[n-i]);
    int inv=qpow(n,mod-2);
    for(int i=0;i<n;i++)a[i]=1ll*a[i]*inv%mod;
}
inline poly operator*(poly a,poly b){
    poly A(a),B(b);
    int n=A.size()+B.size();
    get(n);
    A.resize(wl);B.resize(wl);
    DIF(A);DIF(B);
    for(int i=0;i<wl;i++)A[i]=1ll*A[i]*B[i]%mod;
    DIT(A);A.resize(n-1);
    return A;
}
void fwt(int a[],int n,int tp){
    for(int mid=1;mid<n;mid<<=1){
        for(int i=0;i<n;i+=mid<<1){
            for(int j=0;j<mid;j++){
                int x=a[i+j],y=a[i+j+mid];
                a[i+j]=1ll*tp*(x+y)%mod;
                a[i+j+mid]=1ll*tp*(x-y+mod)%mod;
            }
        }
    }
}
int main(){
    scanf("%d%d%d",&n,&m,&k);init(n<<1);
    for(int i=1;i<=n;i++){
        int x;scanf("%d",&x);b[x]++;
    }
    fwt(b,k,1);
    poly f(n),g(n);
    for(int i=0;i<=m;i++){
        if(i&1)f[i]=1ll*inv[i]*(mod-inv[m-i])%mod;
        else f[i]=1ll*inv[i]*inv[m-i]%mod;
    }
    for(int i=0;i<=n-m;i++)g[i]=1ll*inv[i]*inv[n-m-i]%mod;
    f=f*g;
    for(int i=0;i<k;i++){
        int y=1ll*(n-b[i]+mod)*inv[2]%mod;
        b[i]=1ll*f[y]*jc[y]%mod*jc[n-y]%mod;
    }
    fwt(b,k,inv[2]);
    for(int i=0;i<k;i++)printf("%d ",b[i]);
    return 0;
}
posted @ 2023-04-27 16:00  gtm1514  阅读(22)  评论(1编辑  收藏  举报