首页 > 其他分享 >【考后总结】6 月西安多校模拟赛 3

【考后总结】6 月西安多校模拟赛 3

时间:2023-06-18 11:55:15浏览次数:30  
标签:考后 int siz 多校 sum2 maxn 端点 Data 模拟

6.17 冲刺国赛模拟 20

T1 树染色

容易发现每种方案都可以变成没有交边的链剖分,在此基础上的方案数是每个链顶的深度,考虑 DP。

直接 DP 大致是维护 \(\prod (\prod a+\prod b)\times dep_{top}\),发现这个东西非常不好转移,转移时需要枚举叶子,复杂度不优秀。

改为设 \(f_{i,0/1}\) 表示从 \(i\) 节点连出的链颜色,这样我们就可以直接枚举 \(u\) 链连入的子树 \(v\),而其他子树的颜色就可以任意选了,这个处理可以先求出所有都任选的个数,再具体计算的时候除去就行了,注意到一个子树内答案是包括顺序的,因此合并子树信息是多重集组合数。

点击查看代码
inline int q_pow(int A,int B,int P){
    int res=1;
    while(B){
        if(B&1) res=1ll*res*A%P;
        A=1ll*A*A%P;
        B>>=1;
    }
    return res;
}

int n;
int inv[maxn],fact[maxn],fact_inv[maxn];
inline int C(int N,int M){
    return 1ll*fact[N]*fact_inv[M]%mod*fact_inv[N-M]%mod;
}
struct edge{
    int to,nxt,a,b;
}e[maxn<<1];
int head[maxn],cnt;
inline void add_edge(int u,int v,int a,int b){
    e[++cnt].to=v,e[cnt].nxt=head[u],e[cnt].a=a,e[cnt].b=b,head[u]=cnt;
    e[++cnt].to=u,e[cnt].nxt=head[v],e[cnt].a=a,e[cnt].b=b,head[v]=cnt;
}
int dep[maxn],siz[maxn];;
int f[maxn][2];
void dfs(int u,int fa,int d){
    dep[u]=d;
    int prodf=1,prodC=1;
    for(int i=head[u];i;i=e[i].nxt){
        int v=e[i].to,a=e[i].a,b=e[i].b;
        if(v==fa) continue;
        dfs(v,u,d+1);
        siz[u]+=siz[v];
        prodf=1ll*prodf*(1ll*f[v][0]*a%mod+1ll*f[v][1]*b%mod)%mod*(dep[u]+1)%mod;
        prodC=1ll*prodC*fact_inv[siz[v]]%mod;
    }
    if(!siz[u]){
        siz[u]=1;
        f[u][0]=f[u][1]=1;
        return;
    }
    for(int i=head[u];i;i=e[i].nxt){
        int v=e[i].to,a=e[i].a,b=e[i].b;
        if(v==fa) continue;
        int nowf=1ll*prodf*q_pow((1ll*f[v][0]*a%mod+1ll*f[v][1]*b%mod)%mod*(dep[u]+1)%mod,mod-2,mod)%mod,nowC=1ll*prodC*fact[siz[u]-1]%mod*siz[v]%mod;
        f[u][0]=(f[u][0]+1ll*f[v][0]*a%mod*nowf%mod*nowC%mod)%mod;
        f[u][1]=(f[u][1]+1ll*f[v][1]*b%mod*nowf%mod*nowC%mod)%mod;
    }
}
int main(){
    freopen("treecolor.in","r",stdin);
    freopen("treecolor.out","w",stdout);
    n=read();
    inv[1]=1;
    for(int i=2;i<=n;++i) inv[i]=1ll*(mod-mod/i)*inv[mod%i]%mod;
    fact[0]=1;
    for(int i=1;i<=n;++i) fact[i]=1ll*fact[i-1]*i%mod;
    fact_inv[0]=1;
    for(int i=1;i<=n;++i) fact_inv[i]=1ll*fact_inv[i-1]*inv[i]%mod;
    for(int i=1;i<n;++i){
        int u=read(),v=read(),a=read(),b=read();
        add_edge(u,v,a,b);
    }
    dfs(1,0,0);
    printf("%d\n",(f[1][0]+f[1][1])%mod);
    return 0;
}

T2 关路灯

发现如果不考虑询问区间的限制,每次改变方向后移动的距离一定大于上次一定总距离的 \(2\) 倍,也就是只会改变 \(O(\log n)\) 次方向,考虑由右到左的条件是 \(a_x-a_{l-1}<a_{x+1}-a_x\),由左到右的条件是 \(a_{r+1}-a_x\ge a_x-a_{x-1}\),整理完是就是找到第一个满足条件的 \(x\),可以二分区间,用 ST 表查最值。

有了区间询问的限制实际上就是第一个超出区间的拐点会变成这个区间的端点,之后再转向就直接走到另一个端点了,因此我们可以计算每个询问区间中的每个位置究竟是先到达哪个端点。

以右端点贡献为例,在初始位置 \(s\) 把左端点加入,这里只加入从左向右的线段,之后每次遇到线段右端点就删除这条线段,进而加入下一条 \(s\) 相同的从左到右的线段,删除前如果当前位置有询问区间右端点就线段树上查询。注意到如果右端点贡献则说明存在一个线段右端点在询问的右侧,但这个线段并不覆盖询问区间,所以就是查询左端点小于询问区间左端点的相关信息,线段树维护答案即可。左端点贡献同理。复杂度 \(O(n\log^2 n+m\log n)\)。

点击查看代码
int n,m;
int a[maxn];
int L[maxm],R[maxm];
int stmx[maxn][maxlog],stmn[maxn][maxlog];
inline void build(){
    for(int i=1;i<=n;++i){
        stmx[i][0]=(i==1)?inf:2*a[i]-a[i-1];
        stmn[i][0]=(i==n)?-inf:2*a[i]-a[i+1];
    }
    for(int k=1;k<=18;++k){
        for(int i=1;i+(1<<k)-1<=n;++i){
            stmx[i][k]=max(stmx[i][k-1],stmx[i+(1<<k-1)][k-1]);
            stmn[i][k]=min(stmn[i][k-1],stmn[i+(1<<k-1)][k-1]);
        }
    }
}
inline int query_max(int l,int r){
    int k=__lg(r-l+1);
    return max(stmx[l][k],stmx[r-(1<<k)+1][k]);
}
inline int query_min(int l,int r){
    int k=__lg(r-l+1);
    return min(stmn[l][k],stmn[r-(1<<k)+1][k]);
}
struct Segment{
    int bel;
    int id,l,r;
    ll len;
    bool type;
    Segment()=default;
    Segment(int bel_,int id_,int l_,int r_,ll len_,bool type_):bel(bel_),id(id_),l(l_),r(r_),len(len_),type(type_){}
};
vector<Segment> Seg[maxn],LS[maxn],RS[maxn];
vector<int> Q[maxn];
struct Data{
    int siz;
    ll sum1,sum2;
    Data()=default;
    Data(int siz_,ll sum1_,ll sum2_):siz(siz_),sum1(sum1_),sum2(sum2_){}
    Data operator+(const Data& rhs)const{
        return Data(siz+rhs.siz,sum1+rhs.sum1,sum2+rhs.sum2);
    }
};
struct SegmentTree{
#define mid ((l+r)>>1)
#define lson rt<<1,l,mid
#define rson rt<<1|1,mid+1,r
    Data val[maxn<<2];
    inline void push_up(int rt){
        val[rt]=val[rt<<1]+val[rt<<1|1];
    }
    void update(int rt,int l,int r,int p,int k1,int k2,int k3){
        if(l==r){
            val[rt].siz+=k1,val[rt].sum1+=k2,val[rt].sum2+=k3;
            return;
        }
        if(p<=mid) update(lson,p,k1,k2,k3);
        else update(rson,p,k1,k2,k3);
        push_up(rt);
    }
    Data query(int rt,int l,int r,int pl,int pr){
        if(pl<=l&&r<=pr) return val[rt];
        Data res=Data(0,0,0);
        if(pl<=mid) res=res+query(lson,pl,pr);
        if(pr>mid) res=res+query(rson,pl,pr);
        return res;
    }
    void clear(int rt,int l,int r){
        val[rt]=Data(0,0,0);
        if(l==r) return;
        clear(lson),clear(rson);
    }
#undef mid
#undef lson
#undef rson
}S;
ll ans[maxm];
int main(){
    freopen("light.in","r",stdin);
    freopen("light.out","w",stdout);
    n=read(),m=read();
    for(int i=1;i<=n;++i) a[i]=read();
    build();
    for(int i=1;i<=m;++i) L[i]=read(),R[i]=read();
    for(int i=1;i<=n;++i){
        int lpos=i,rpos=i;
        if(i==1){
            Seg[i].push_back(Segment(i,0,1,1,0,0));
            Seg[i].push_back(Segment(i,1,1,n,0,1));
            continue;
        }
        if(i==n){
            Seg[i].push_back(Segment(i,0,n,n,0,1));
            Seg[i].push_back(Segment(i,1,1,n,0,0));
            continue;
        }
        bool type=(a[i+1]-a[i]<=a[i]-a[i-1]);
        Seg[i].push_back(Segment(i,0,i,i,0,type^1));
        int cnt=0;
        ll sum=0;
        while(1){
            if(type){
                int l=rpos+1,r=n;
                int now;
                while(l<=r){
                    int mid=(l+r)>>1;
                    if(query_min(rpos+1,mid)<a[lpos-1]) now=mid,r=mid-1;
                    else l=mid+1;
                }
                rpos=now;
                ++cnt;
                Seg[i].push_back(Segment(i,cnt,lpos,rpos,sum,type));
                sum+=a[rpos]-a[lpos];
                if(rpos==n){
                    ++cnt;
                    Seg[i].push_back(Segment(i,cnt,1,n,sum,type^1));
                    sum+=a[rpos]-a[lpos];
                    break;
                } 
                rpos=now;
            }
            else{
                int l=1,r=lpos-1;
                int now;
                while(l<=r){
                    int mid=(l+r)>>1;
                    if(query_max(mid,lpos-1)>=a[rpos+1]) now=mid,l=mid+1;
                    else r=mid-1;
                }
                lpos=now;
                ++cnt;
                Seg[i].push_back(Segment(i,cnt,lpos,rpos,sum,type));
                sum+=a[rpos]-a[lpos];
                if(lpos==1){
                    ++cnt;
                    Seg[i].push_back(Segment(i,cnt,1,n,sum,type^1));
                    sum+=a[n]-a[1];
                    break;
                }
            }
            type^=1;
        }
    }
    for(int i=1;i<=n;++i){
        if(Seg[i][0].type){
            LS[Seg[i][0].l].push_back(Seg[i][0]);
            RS[Seg[i][0].r].push_back(Seg[i][0]);
        }
        else{
            LS[Seg[i][1].l].push_back(Seg[i][1]);
            RS[Seg[i][1].r].push_back(Seg[i][1]);
        }
    }
    for(int i=1;i<=m;++i){
        Q[R[i]].push_back(i);
    }
    for(int i=1;i<=n;++i){
        for(Segment s:LS[i]){
            S.update(1,1,n,i,1,a[s.l],s.len);
        }
        for(int j:Q[i]){
            if(L[j]<n){
                Data now=S.query(1,1,n,L[j]+1,n);
                ans[j]+=1ll*now.siz*(a[R[j]]-a[L[j]]);
                ans[j]+=1ll*now.siz*a[R[j]]-now.sum1;
                ans[j]+=now.sum2;
            }
        }
        for(Segment s:RS[i]){
            S.update(1,1,n,s.l,-1,-a[s.l],-s.len);
            if(s.id+2<Seg[s.bel].size()){
                Segment nxt=Seg[s.bel][s.id+2];
                S.update(1,1,n,nxt.l,1,a[nxt.l],nxt.len);
                RS[nxt.r].push_back(nxt);
            }
        }
    }
    S.clear(1,1,n);
    for(int i=1;i<=n;++i){
        LS[i].clear();
        RS[i].clear();
        Q[i].clear();
    }
    for(int i=1;i<=n;++i){
        if(!Seg[i][0].type){
            LS[Seg[i][0].l].push_back(Seg[i][0]);
            RS[Seg[i][0].r].push_back(Seg[i][0]);
        }
        else{
            LS[Seg[i][1].l].push_back(Seg[i][1]);
            RS[Seg[i][1].r].push_back(Seg[i][1]);
        }
    }
    for(int i=1;i<=m;++i){
        Q[L[i]].push_back(i);
    }
    for(int i=n;i>=1;--i){
        for(Segment s:RS[i]){
            S.update(1,1,n,i,1,a[s.r],s.len);
        }
        for(int j:Q[i]){
            if(R[j]>1){
                Data now=S.query(1,1,n,1,R[j]-1);
                ans[j]+=1ll*now.siz*(a[R[j]]-a[L[j]]);
                ans[j]+=now.sum1-1ll*now.siz*a[L[j]];
                ans[j]+=now.sum2;
            }
        }
        for(Segment s:LS[i]){
            S.update(1,1,n,s.r,-1,-a[s.r],-s.len);
            if(s.id+2<Seg[s.bel].size()){
                Segment nxt=Seg[s.bel][s.id+2];
                S.update(1,1,n,nxt.r,1,a[nxt.r],nxt.len);
                LS[nxt.l].push_back(nxt);
            }
        }
    }
    for(int i=1;i<=m;++i) printf("%lld\n",ans[i]);
    return 0;
}

标签:考后,int,siz,多校,sum2,maxn,端点,Data,模拟
From: https://www.cnblogs.com/SoyTony/p/Multiple_School_Simulation_Problems_in_Xian_June_3.html

相关文章

  • farm (牛客多校) (二维树状+数学式子优化+rand()去除特殊情况)
    题目大意:给出一个n*m的田地矩阵,每个格子上种着一种植物。给格子施肥t次,每一次给出五个数字,x1,y1,x2,y2,k,要施肥的区域坐标和要施的肥料种类。如果植物和施肥种类不匹配,植物会死亡。问最终会死多少个植物。 思路:判断一个植物死不死, 判断植物种类*施肥次数==施肥种类总和某......
  • 2023冲刺国赛模拟20
    2023冲刺国赛模拟20越来越废物了。A.树染色\(f_{x,1/0}\)表示考虑\(x\)子树内,第一条链为黑色/白色,不考虑第一条链在子树外方案数的答案。转移枚举第一条链是哪个,用组合数给各个子树的链定序。code#include<bits/stdc++.h>usingnamespacestd;typedeflonglong......
  • 2023冲刺国赛模拟 20.1
    我是越来越摆了,一下午就改了一道题;而且这么菜,看了半天的题解做法还没看懂。又是被暴踩的一天。T1树染色比较套路的想法是考虑当前以\(u\)为根的子树,考虑第一次选择\(u\)子树内的叶节点,此时我们必须选择\(u\)以上的节点作为链顶,这会对方案数产生贡献。考虑通过第一条链划......
  • 阻尼涂层的细长板的共振数值模拟
    摘要精简讨论了薄壁结构弯曲振动的表面阻尼的经典方法,即阻尼涂层由两层具有显著粘弹性的材料和一层高模量材料的中间薄增强层组成。考虑到阻尼层的横向压缩,建立了具有整体阻尼涂层的细长板的四层有限元,建立了一个有限元控制方程组。材料特性1.作为阻尼涂层的材料,使用的是以......
  • car (牛客多校) (情景找规律,抠细节)
    题目大意:给一个正方形棋盘,你现在可以在棋盘的边缘防止车,然后车只能向正对的方向走,(角落可以往2边走)2个车相遇会G给m个破环的方块,车经过就G问最多可以放多少个车] 思路:注意奇偶分规律,偶数2*n,奇数2*n-1注意放置破环的方块,在奇数最中间的时候,......
  • stress模拟系统负载较高时的场景
    #!/bin/bash#获取网卡名称network_adapter=`ipa|grep"BROADCAST"|awk'{print$2}'|awk-F:'{print$1}'`functionNet_speed_limiting{#输入提示read-p"PleaseinputNetspeedlimitingrate(kbit):"Net_raterea......
  • 基于Matlab模拟时变瑞利衰落信道中的差分放大转发中继
    ✅作者简介:热爱科研的Matlab仿真开发者,修心和技术同步精进,matlab项目合作可私信。......
  • 【光学】基于matlab模拟光纤布拉格光栅FBG反射谱和透射谱仿真
    ✅作者简介:热爱科研的Matlab仿真开发者,修心和技术同步精进,matlab项目合作可私信。......
  • 基于元胞自动机模拟森林火灾附matlab代码
    ✅作者简介:热爱科研的Matlab仿真开发者,修心和技术同步精进,matlab项目合作可私信。......
  • 计组期末模拟(补充)
    单选题2-1(本题考查课程目标2)某计算机有16个通用寄存器,采用32位定长指令字,操作码字段(含寻址方式位)为8位,Store指令的源操作数和目的操作数分别采用寄存器直接寻址和基址寻址方式。若基址寄存器可使用任一通用寄存器,且偏移量用补码表示,则Store指令中偏移量的取值范围是(......