NOI模拟20220519

清北营前的倒数第二场考试,仍然不太理想

开场看T1,很快就搞到了50pts,想要优化成正解,我很清楚正解的复杂度一定和本质不同的gcd个数有关

也想到了那个dp,但是我不会O(n)转移,于是祭了

然后看T2,wcnmd数据范围是这个??,SG函数,写状压?线性都不行,算啦算啦弃了,于是没写,考后被波波疯狂diss

T3确实是有一点眼熟哈,然而并没有下文了,只会\(n^4\),然鹅小小的把转移集中一下就是正解了

原题是陈雨昕的原创,复杂度是\(n^2\)的,用了概率的性质,对贡献进行拆分,每次转移平均值,是我见过的dp最登峰造极的了

T1 调兵遣将

就是那个DP数组可以O(n)求,然后每次提取关键点,复杂度就对了

AC_code
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define fo(i,x,y) for(int i=(x);i<=(y);i++)
#define fu(i,x,y) for(int i=(x);i>=(y);i--)
int read(){
    int s=0,t=1;char ch=getchar();
    while(!isdigit(ch)){if(ch=='-')t=-1;ch=getchar();}
    while(isdigit(ch)){s=(s<<1)+(s<<3)+(ch^48);ch=getchar();}
    return s*t;
}
const int N=50005;
const int mod=998244353;
int ksm(int x,int y){
    int ret=1;
    while(y){
        if(y&1)ret=ret*x%mod;
        x=x*x%mod;y>>=1;
    }return ret;
}
int n,w[N],ans[N],cnm;
struct BIT{
    int tr1[N],tr2[N],lm;
    void init(int x){lm=x;fo(i,1,lm)tr1[i]=tr2[i]=0;}
    void insert(int x,int v){
        for(int i=x;i<=lm;i+=(i&-i)){
            tr1[i]=(tr1[i]+v)%mod;
            tr2[i]=(tr2[i]+v*(x-1))%mod;
        }
    }
    void ins(int l,int r,int v){
        insert(l,v);insert(r+1,-v);
    }
    int query(int x){
        int ret=0;
        for(int i=x;i;i-=(i&-i))
            ret=(ret+tr1[i]*x-tr2[i]+mod)%mod;
        return ret;
    }
    int qry(int l,int r){return (query(r)-query(l-1)+mod)%mod;}
}pre,suf;
int gcd(int x,int y){return !y?x:gcd(y,x%y);}
struct XDS{
    #define ls x<<1
    #define rs x<<1|1
    int gd[N*4];
    void pushup(int x){
        gd[x]=gcd(gd[ls],gd[rs]);
    }
    void build(int x,int l,int r){
        if(l==r)return gd[x]=w[l],void();
        int mid=l+r>>1;
        build(ls,l,mid);build(rs,mid+1,r);
        pushup(x);
    }
    int queryr(int x,int l,int r,int ql,int v){
        if(ql<=l){
            if(gd[x]%v==0)return r;
            if(l==r)return l-1;
            int mid=l+r>>1;
            if(gd[ls]%v==0)return queryr(rs,mid+1,r,ql,v);
            else return queryr(ls,l,mid,ql,v);
        }
        int mid=l+r>>1,ret=0;
        if(ql<=mid)ret=queryr(ls,l,mid,ql,v);
        if(!ret||ret==mid)ret=queryr(rs,mid+1,r,ql,v);
        return ret;
    }
    int queryl(int x,int l,int r,int qr,int v){
        if(qr>=r){
            if(gd[x]%v==0)return l;
            if(l==r)return r+1;
            int mid=l+r>>1;
            if(gd[rs]%v==0)return queryl(ls,l,mid,qr,v);
            else return queryl(rs,mid+1,r,qr,v);
        }
        int mid=l+r>>1,ret=0;
        if(qr>mid+1)ret=queryl(rs,mid+1,r,qr,v);
        if(!ret||ret==mid+1)ret=queryl(ls,l,mid,qr,v);
        return ret;
    }
    #undef ls
    #undef rs
}xds;
int lshw[N*30],lhw;
struct nodel{int l,r1,r2,w;};
bool cmpl(nodel a,nodel b){return a.l>b.l;}
vector<nodel> bl[N],ul[N*30];
struct noder{int l1,l2,r,w;};
bool cmpr(noder a,noder b){return a.r<b.r;}
vector<noder> br[N],ur[N*30];
int lsh[N*30],lh;
int f[N*30],pf[N*30],cf[N*30];
int g[N*30],pg[N*30],cg[N*30];
void sol(int x){
    // cerr<<x<<" "<<lshw[x]<<endl;
    lh=0;lsh[++lh]=n+1;
    for(nodel i:ul[x])lsh[++lh]=i.l,lsh[++lh]=i.r1,lsh[++lh]=i.r2;
    for(noder i:ur[x])lsh[++lh]=i.r,lsh[++lh]=i.l1,lsh[++lh]=i.l2;
    sort(lsh+1,lsh+lh+1);lh=unique(lsh+1,lsh+lh+1)-lsh-1;lh--;
    for(nodel &i:ul[x]){
        // cerr<<"ul"<<" "<<x<<" "<<i.l<<" "<<i.r1<<" "<<i.r2<<endl;
        i.l=lower_bound(lsh+1,lsh+lh+1,i.l)-lsh;
        // cerr<<i.l<<endl;
        i.r1=lower_bound(lsh+1,lsh+lh+1,i.r1)-lsh;
        i.r2=lower_bound(lsh+1,lsh+lh+1,i.r2)-lsh;
    }
    // cerr<<ul[x][0].l<<" "<<ul[x][1].l
    for(noder &i:ur[x]){
        // cerr<<"ur"<<" "<<x<<" "<<i.l1<<" "<<i.l2<<" "<<i.r<<endl;
        i.r=lower_bound(lsh+1,lsh+lh+1,i.r)-lsh;
        i.l1=lower_bound(lsh+1,lsh+lh+1,i.l1)-lsh;
        i.l2=lower_bound(lsh+1,lsh+lh+1,i.l2)-lsh;
    }
    fo(i,0,lh+1)f[i]=cf[i]=pf[i]=g[i]=cg[i]=pg[i]=0;
    f[0]=1;pf[0]=lsh[1];
    g[lh+1]=1;pg[lh+1]=lsh[lh+1]-lsh[lh];
    // cerr<<"CM"<<" "<<ul[x][1].l<<endl;
    sort(ul[x].begin(),ul[x].end(),cmpl);
    // cerr<<"SB"<<" "<<ul[x][0].l<<endl;
    sort(ur[x].begin(),ur[x].end(),cmpr);
    int it=0,sz=ur[x].size();
    fo(i,1,lh){
        while(it<sz&&ur[x][it].r==i){
            // cerr<<ur[x][it].r<<endl;
            f[i]=(f[i]+pf[ur[x][it].l2-1]-pf[ur[x][it].l1-1]+f[ur[x][it].l1-1]+mod)%mod;
            it++;
        }
        f[i]=(f[i-1]+f[i])%mod;
        // cerr<<f[i]<<" ";
        pf[i]=(pf[i-1]+f[i]*(lsh[i+1]-lsh[i]))%mod;
    }
    // cerr<<endl;
    it=0;sz=ul[x].size();
    fu(i,lh,1){
        // cerr<<ul[x][1].l<<endl;
        while(it<sz&&ul[x][it].l==i){
            g[i]=(g[i]+pg[ul[x][it].r1+1]-pg[ul[x][it].r2+1]+g[ul[x][it].r2+1]+mod)%mod;
            it++;
        }
        g[i]=(g[i+1]+g[i])%mod;
        // cerr<<g[i]<<" ";
        pg[i]=(pg[i+1]+g[i]*(lsh[i]-lsh[i-1]))%mod;
    }
    // cerr<<endl;
    fo(i,1,lh+1){
        // cerr<<lsh[i]<<" "<<ans[lsh[i]]<<" "<<f[i-1]<<" "<<g[i]<<" ";
        ans[lsh[i-1]+1]=(ans[lsh[i-1]+1]+f[i-1]*g[i])%mod;
        ans[lsh[i]]=(ans[lsh[i]]-f[i-1]*g[i]%mod+mod)%mod;
        // cerr<<ans[lsh[i]]<<endl;
        if(i==lh+1)break;
        ans[lsh[i]]=(ans[lsh[i]]+f[i-1]*g[i+1])%mod;
        ans[lsh[i]+1]=(ans[lsh[i]+1]-f[i-1]*g[i+1]%mod+mod)%mod;
    }
    // fo(i,1,n)cerr<<ans[i]<<" ";cerr<<endl;
    cnm=(cnm+f[lh])%mod;
}
signed main(){
    n=read();
    fo(i,1,n)w[i]=read();
    xds.build(1,1,n);
    fo(l,1,n){
        int las=l,gg=w[l],now;
        while(las<=n){
            gg=gcd(gg,w[las]);lshw[++lhw]=gg;
            now=xds.queryr(1,1,n,las,gg);
            bl[l].emplace_back(nodel{l,las,now,gg});
            las=now+1;
        }
    }
    fo(r,1,n){
        int las=r,gg=w[r],now;
        while(las>=1){
            gg=gcd(gg,w[las]);lshw[++lhw]=gg;
            now=xds.queryl(1,1,n,las,gg);
            br[r].emplace_back(noder{now,las,r,gg});
            las=now-1;
        }
    }
    sort(lshw+1,lshw+lhw+1);lhw=unique(lshw+1,lshw+lhw+1)-lshw-1;
    fo(i,1,n){
        for(nodel j:bl[i]){
            int ps=lower_bound(lshw+1,lshw+lhw+1,j.w)-lshw;
            ul[ps].push_back(j);
        }
        for(noder j:br[i]){
            int ps=lower_bound(lshw+1,lshw+lhw+1,j.w)-lshw;
            ur[ps].push_back(j);
        }
    }
    fo(i,1,lhw)sol(i);
    fo(i,1,n)ans[i]=(ans[i]+ans[i-1])%mod;
    fo(i,1,n)ans[i]=(cnm-ans[i]+mod)%mod;
    fo(i,1,n)printf("%lld ",ans[i]);
}

T2 一掷千金

似乎SG的题好像都能拆分,然后异或起来,这个也行,就是每个白点的异或

然后不知道如何证明白点的SG是啥,题解告诉我是lowbit(max(x,y))

于是我们可以运用牛逼线段树+扫描线,可是我们怎么维护lowbit的异或和呢

发现我们可以建立一颗完全二叉树的线段树,这样的话每个完整区间的答案可以O(1)得到,值域[0,1<<30)

这样我们就可以方便的维护答案了,注意我们是求矩形的并,而不是矩形的叠加,我们只能算有值的位置的答案...

线段树写起来非常的奇妙,体会一下,美丽的线段树!

AC_code
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define fo(i,x,y) for(int i=(x);i<=(y);i++)
#define fu(i,x,y) for(int i=(x);i>=(y);i--)
int read(){
    int s=0,t=1;char ch=getchar();
    while(!isdigit(ch)){if(ch=='-')t=-1;ch=getchar();}
    while(isdigit(ch)){s=(s<<1)+(s<<3)+(ch^48);ch=getchar();}
    return s*t;
}
const int N=1e5+5;
struct XDS{
    struct POT{int ls,rs,sz,tg,as;}tr[N*80];
    int seg;
    void pushup(int x,int l,int r){
        tr[x].sz=tr[tr[x].ls].sz+tr[tr[x].rs].sz;
        if(tr[x].tg)tr[x].sz=r-l+1;
        tr[x].as=tr[tr[x].ls].as^tr[tr[x].rs].as;
        if(tr[x].tg)tr[x].as=(l==r?(l&-l):((l&-l)^((r-l>>1)+1)));
    }
    void ins(int &x,int l,int r,int ql,int qr,int v){
        if(ql>qr)return ;
        if(!x)x=++seg;
        if(ql<=l&&r<=qr){
            tr[x].tg+=v;
            if(tr[x].tg)tr[x].sz=r-l+1;
            else tr[x].sz=tr[tr[x].ls].sz+tr[tr[x].rs].sz;
            if(tr[x].tg)tr[x].as=(l==r?(l&-l):((l&-l)^((r-l>>1)+1)));
            else tr[x].as=tr[tr[x].ls].as^tr[tr[x].rs].as;
            return ;
        }
        int mid=l+r>>1;
        if(ql<=mid)ins(tr[x].ls,l,mid,ql,qr,v);
        if(qr>mid)ins(tr[x].rs,mid+1,r,ql,qr,v);
        pushup(x,l,r);
    }
    int qsz(int x,int l,int r,int ql,int qr,int ok){
        if(ql>qr)return 0;ok|=tr[x].tg;
        if(ql<=l&&r<=qr)return ok?r-l+1:tr[x].sz;
        int mid=l+r>>1,ret=0;
        if(ql<=mid)ret+=qsz(tr[x].ls,l,mid,ql,qr,ok);
        if(qr>mid)ret+=qsz(tr[x].rs,mid+1,r,ql,qr,ok);
        pushup(x,l,r);return ret;
    }
    int qas(int x,int l,int r,int ql,int qr,int ok){
        if(ql>qr)return 0;ok|=tr[x].tg;
        if(ql<=l&&r<=qr)return ok?(l==r?(l&-l):((l&-l)^((r-l>>1)+1))):tr[x].as;
        int mid=l+r>>1,ret=0;
        if(ql<=mid)ret^=qas(tr[x].ls,l,mid,ql,qr,ok);
        if(qr>mid)ret^=qas(tr[x].rs,mid+1,r,ql,qr,ok);
        pushup(x,l,r);return ret;
    }
}xds;
int T,n,m,rt,ans;
struct line{int ya,yb;};
vector<line> add[N],del[N];
signed main(){
    T=read();n=read();m=read();
    fo(i,1,T){
        int xa=read(),ya=read(),xb=read(),yb=read();
        add[xa].push_back(line{ya,yb});
        del[xb+1].push_back(line{ya,yb});
    }
    fo(i,1,n){
        for(line x:add[i])xds.ins(rt,0,(1<<30)-1,x.ya,x.yb,1);
        for(line x:del[i])xds.ins(rt,0,(1<<30)-1,x.ya,x.yb,-1);
        if(xds.qsz(rt,0,(1<<30)-1,0,i,0)&1)ans^=(i&-i);
        ans^=xds.qas(rt,0,(1<<30)-1,i+1,m,0);
        // cerr<<ans<<endl;
    }
    printf("%lld\n",ans);
}

T3 树拓扑序

吐槽:出题人这是啥垃圾拓扑序,反着的???

第一反应,拆分一下,拆成每一个顺序对对答案的贡献,发现这个可以直接枚举然后在LCA处合并一下,复杂度\(O(n^4)\)

并不能通过,于是我们观察这个式子,发现可以枚举x,跳的同时统计所有大于x的方案数(后缀和),于是就可以通过了

那个\(O(n^2)\)的做法是这样的,我们定义f[u][v]表示u子树和v子树合并起来的逆序对平均数

我们转移的时候可以拆分成u的几个儿子和v合并的答案加上u和v合并的答案,几个儿子的答案由另外的数组得出,剩下的直接看是u在第一位还是v在第一位就好了...

AC_code
#include<bits/stdc++.h>
using namespace std;
// #define int long long
#define fo(i,x,y) for(int i=(x);i<=(y);i++)
#define fu(i,x,y) for(int i=(x);i>=(y);i--)
int read(){
    int s=0,t=1;char ch=getchar();
    while(!isdigit(ch)){if(ch=='-')t=-1;ch=getchar();}
    while(isdigit(ch)){s=(s<<1)+(s<<3)+(ch^48);ch=getchar();}
    return s*t;
}
const int N=505;
const int mod=1e9+7;
int ksm(int x,int y){
    int ret=1;
    while(y){
        if(y&1)ret=1ll*ret*x%mod;
        x=1ll*x*x%mod;y>>=1;
    }return ret;
}
int n,ans;
vector<int> e[N];
int jc[N],inv[N];
int C(int x,int y){return 1ll*jc[x]*inv[y]%mod*inv[x-y]%mod;}
int D(int x,int y){return 1ll*inv[x]*jc[y]%mod*jc[x-y]%mod;}
int fa[N],dep[N],sz[N];
int dp[N][N][N],g[N],ig[N];
void dfs_pre(int x,int f){
    fa[x]=f;dep[x]=dep[f]+1;
    for(int y:e[x])if(y!=f)dfs_pre(y,x);
}
void dfs_g(int x,int f){
    sz[x]=0;g[x]=1;
    for(int y:e[x])if(y!=f){
        dfs_g(y,x);sz[x]+=sz[y];
        g[x]=1ll*g[x]*g[y]%mod*C(sz[x],sz[y])%mod;
    }sz[x]++;
}
int h[N];
void get_dp(int v){
    dp[v][v][1]=g[v];int cv=v;
    while(fa[v]){
        int x=fa[v],now=0;
        fo(i,1,sz[x])h[i]=0;h[0]=1;
        for(int y:e[x])if(y!=fa[x]&&y!=v){
            fu(i,now,0){
                fo(j,1,sz[y]){
                    h[i+j]=(h[i+j]+1ll*h[i]*C(i+j,j)%mod*C(now-i+sz[y]-j,sz[y]-j)%mod*g[y])%mod;
                }
                h[i]=1ll*h[i]*C(now-i+sz[y],sz[y])%mod*g[y]%mod;
            }
            now+=sz[y];
        }
        fo(i,1,sz[v])if(dp[cv][v][i]){
            fo(j,i,i+now){
                dp[cv][x][j+1]=(dp[cv][x][j+1]+1ll*dp[cv][v][i]*h[j-i]%mod*C(j-1,i-1)%mod*C(sz[x]-1-j,now-j+i))%mod;
            }
        }
        v=fa[v];
    }
}
int LCA(int x,int y){
    if(dep[x]<dep[y])swap(x,y);
    while(dep[x]>dep[y])x=fa[x];
    while(x!=y)x=fa[x],y=fa[y];
    return x;
}
int tp[N],bh[N][N][N];
int sol(int x,int y){
    int lca=LCA(x,y);
    if(lca==x)return g[1];
    else if(lca==y)return 0;
    int nx=x,ny=y,ret=0;
    while(fa[nx]!=lca)nx=fa[nx];
    while(fa[ny]!=lca)ny=fa[ny];
    fo(i,1,sz[nx])if(dp[x][nx][i]){
        fo(k,0,sz[ny]-1)tp[k]=1ll*dp[x][nx][i]%mod*C(k+i-1,i-1)%mod*C(sz[nx]-i+sz[ny]-k,sz[nx]-i)%mod;
        fo(k,1,sz[ny]-1)tp[k]=(tp[k]+tp[k-1])%mod;
        fo(j,1,sz[ny])if(dp[y][ny][j]){
            ret=(ret+1ll*tp[j-1]*dp[y][ny][j])%mod;
        }
    }
    int now=1ll*g[lca]*D(sz[lca],sz[nx])%mod*D(sz[lca]-sz[nx],sz[ny])%mod*ig[nx]%mod*ig[ny]%mod;
    ret=1ll*ret*now%mod*C(sz[lca],sz[nx]+sz[ny])%mod;
    while(fa[lca]){
        int x=fa[lca];
        ret=1ll*ret*g[x]%mod*ig[lca]%mod;
        lca=fa[lca];
    }
    return ret;
}
int work(int x){
    int ret=0,res=0,nx=x;
    fo(y,x+1,n)if(LCA(x,y)==x)res=(res+g[x])%mod;
    while(fa[nx]){
        int f=fa[nx],tmp=0;
        res=1ll*res*g[f]%mod*ig[nx]%mod;
        for(int ny:e[f])if(ny!=fa[f]&&ny!=nx){
            int ttm=0;
            fo(i,1,sz[nx])if(dp[x][nx][i]){
                fo(k,0,sz[ny]-1)tp[k]=1ll*dp[x][nx][i]%mod*C(k+i-1,i-1)%mod*C(sz[nx]-i+sz[ny]-k,sz[nx]-i)%mod;
                fo(k,1,sz[ny]-1)tp[k]=(tp[k]+tp[k-1])%mod;
                fo(j,1,sz[ny])if(bh[x+1][ny][j]){
                    ttm=(ttm+1ll*tp[j-1]*bh[x+1][ny][j])%mod;
                }
            }
            int now=1ll*g[f]*D(sz[f],sz[nx])%mod*D(sz[f]-sz[nx],sz[ny])%mod*ig[nx]%mod*ig[ny]%mod;
            ttm=1ll*ttm*now%mod*C(sz[f],sz[nx]+sz[ny])%mod;
            res=(res+ttm)%mod;
        }nx=f;
    }
    return res;
}
signed main(){
    n=read();
    jc[0]=1;fo(i,1,n)jc[i]=1ll*jc[i-1]*i%mod;
    inv[0]=1;inv[n]=ksm(jc[n],mod-2);
    fu(i,n-1,1)inv[i]=1ll*inv[i+1]*(i+1)%mod;
    fo(i,1,n-1){
        int x=read(),y=read();
        e[x].push_back(y);e[y].push_back(x);
    }
    dfs_pre(1,0);dfs_g(1,0);
    fo(i,1,n)ig[i]=ksm(g[i],mod-2);
    fo(i,1,n)get_dp(i);
    fu(i,n,1)fo(j,1,n)fo(k,1,n)bh[i][j][k]=(bh[i+1][j][k]+dp[i][j][k])%mod;
    // cerr<<"SB"<<" "<<1.0*clock()/CLOCKS_PER_SEC<<endl;
    // fo(i,1,n)fo(j,i+1,n)ans=(ans+sol(i,j))%mod;
    fo(i,1,n)ans=(ans+work(i))%mod;
    printf("%d\n",ans);
    return 0;
}
posted @ 2022-05-19 20:58  fengwu2005  阅读(40)  评论(0编辑  收藏  举报