星云俱乐部招新赛做题笔记

星云俱乐部招新赛做题笔记

语文(chinese)

简单题,考虑直接对有 \(m\) 条边的图跑 \(\text{Kruskal}\),求出最小生成树之后,容易发现,每删掉一条边,就会将图多分成两个部分。

所以只需将这颗最小生成树删去最长的 \(k-1\) 条边,就是最终答案。

赛时人均 AC 了。


int cnt=0;
for(int i=1;i<=m;i++){ //kruskal
    int u=e[i].x,v=e[i].y,w=e[i].w;

    int fu=Find(u),fv=Find(v);
    if(fu==fv)continue;

    fa[fu]=fv;
    sum+=w,ans[++cnt]=w;
}

sort(ans+1,ans+1+cnt);
for(int i=cnt;i>=cnt-k+2;i--){ //删边
    sum-=ans[i];
}

cout<<sum<<"\n";

政治(politics)

对于 \(k=1\),只存在一种方案,直接判断其是否可行 。

然后对于 \(k\geq 2\) 进行分类讨论:

  • \(o=1\),即只需要满足一半限制。

由于只有两种限制:在同一天复习或不在同一天复习,那么仅需全部满足个数较多的限制即可。

如果要求同一天复习的天数较多,那么仅需全部放在同一天复习;

如果不能在同一天复习的天数较多,那么可以使用 \(01\) 染色,保证相邻颜色不同。

  • \(o=2\),需要满足 \(3/4\) 的限制,考虑 \(3/4\) 这个数字的特点,容易发现只要先满足所有行的限制,再满足一半列的限制就行。

注意,如果在满足所有行的限制后,列无法满足一半的限制,那么只需要将所有行间染色全部反转,那么容易证明,一定可以满足限制。

所以 \(k\geq 2\) 的情况,一定有解。

特判 \(k=1\) 的情况:

赛时没有想出构造方案

int cnt=0;
for(int i=1;i<=n;i++)
    for(int j=1;j<m;j++){
        cin>>row[i][j];
        cnt+=row[i][j];        
    }
for(int i=1;i<n;i++)
    for(int j=1;j<=m;j++){
        cin>>col[i][j];
        cnt+=col[i][j];
    }
    
int tot=2*n*m-m-n;
if(check(tot,cnt)){
    cout<<"win!\n";
    for(int i=1;i<=n;i++){
        for(int j=1;j<=m;j++)
            cout<<"1 ";
        cout<<"\n";
    }
    return;
}
else{
    cout<<"lost!\n";
    return;
}

\(k \geq 2\) 的情况:

if(n>m){  //当 n>m 时,需要将这个矩阵翻转
    flag_swap=1;
    for(int i=1;i<=n;i++)
        for(int j=1;j<m;j++){
            cin>>col[j][i];
        }
    for(int i=1;i<n;i++)
        for(int j=1;j<=m;j++){
            cin>>row[j][i];
        }
    swap(n,m);
}
else{
    for(int i=1;i<=n;i++)
        for(int j=1;j<m;j++){
            cin>>row[i][j];
        }
    for(int i=1;i<n;i++)
        for(int j=1;j<=m;j++){
            cin>>col[i][j];
        }
}

for(int i=n;i>=1;i--){
    ans[i][m]=1;
    for(int j=m-1;j>=1;j--){
        ans[i][j]=row[i][j]? 3-ans[i][j+1] : ans[i][j+1];
    }    //满足行的限制
    
    //判断一半列的限制
    int cur=0;
    for(int j=1;j<=m;j++){
        cur+=((ans[i][j]!=ans[i+1][j])^col[i][j]);
    }
    if(cur>m/2){
        for(int j=1;j<=m;j++)
            ans[i][j]=3-ans[i][j];
    }
        
}

/*输出*/

学校

\(f_{i,j}\) 表示 \(p_k=i\)\(a_{p_k-1} \oplus a_{p_k}=j\) 的方案数。

\(all_i\) 表示以 \(i\) 结尾的不合法序列的数量,转移中再减去 \(all_{p_{k-1}}\)

转移方程:\(f_{i,j}=2^{p_{k-1}-1}-\sum_{k=1}^{p_{k-1}-1}f_{k,j\oplus s}-all_{p_{k-1}}\)

ll slove(){
    pw_2[0]=1;
    for(int i=1;i<=n;i++)
        pw_2[i]=(pw_2[i-1]*2)%mod;
    for(int i=1;i<=n;i++)
        pos[a[i]]=i;
        
    ll ans=0;
    for(int i=1;i<=n;i++){
        for(int j=0;j<(1<<m);j++){
            ll cur=pos[j^a[i]];
            
            if(!cur||cur>=i){
                f[i][j]=0;
            }
            else{
                f[i][j]=(pw_2[cur-1]-g[cur-1][s^j]-all[cur]+mod)%mod;
                all[i]=(all[i]+g[cur-1][s^j]+all[cur]+mod)%mod;
            }
        }
        for(int j=0;j<(1<<m);j++)
            g[i][j]=(g[i-1][j]+f[i][j]+mod)%mod;
        
        ans=(ans+1+mod)%mod;
        for(int j=0;j<(1<<m);j++){
            ans=(ans+f[i][j]+mod)%mod;
        }
    }

    return ans%mod;
}

posted @ 2025-01-13 21:42  SunburstFan1106  阅读(25)  评论(0)    收藏  举报