The 3rd Universal Cup. Stage 24: Poland

赛时切了ABGIJK,觉得有价值需要记录一下的只有这两道题。

B

题意简述

没有,简述不了。

思路简述

不妨记 \(f_{0/1/2,i,j}\) 表示从 \(i\) 到 \(j\) 至少携带的行李数是 \(0,1,2\) 个,这时从 \(i\) 到 \(j\) 的最短路。这个东西可以直接通过 floyd 算法快速维护。

于是从 \(i\) 到 \(j\) 的行李搬运就有以下三种情况:

  1. 直接通过携带 2 个行李的路径从 \(i\) 走到 \(j\),也就是 \(f_{2,i,j}\)
  2. 首先携带 2 个行李到某个点 \(k\),然后通过 \(k\) 搬运两次走到 \(j\),也就是 \(f_{2,i,k}+f_{1,k,j}+f_{0,j,k}+f_{1,k,j}\)
  3. 首先通过搬运两次到某个中间点 \(k\),然后携带两个行李直接到 \(j\),也就是 \(f_{1,i,k}+f_{0,k,i}+f_{1,i,k}+f_{2,k,j}\)

将以上情况处理出来的情况称作 \(g_{i,j}\),这里也就处理出了任意两点之间搬运两个行李的最小贡献。

再跑一轮 floyd 也就得到了任意两点之间搬运行李的最短路。

综上,时间复杂度为 \(O(n^3)\)

代码

#include<bits/stdc++.h>
using ll=long long;
using namespace std;
const int N=404;
ll f[3][N][N];
ll g[N][N];
const ll inf=10000000000000999;
void Silverwolf(){
    int n,m;
    cin>>n>>m;
    for(int k=0;k<=2;k++){
        for(int i=1;i<=n;i++){
            for(int j=1;j<=n;j++){
                f[k][i][j]=inf;
                g[i][j]=inf;
            }
        }
    }
    for(int i=1;i<=m;i++){
        int a,b,c,d;
        cin>>a>>b>>c>>d;
        for(int k=0;k<=d;k++){
            f[k][a][b]=min(f[k][a][b],1ll*c);
        }
    }
    
    for(int t=0;t<=2;t++){
        for(int i=1;i<=n;i++)f[t][i][i]=0;
    }
    for(int t=0;t<=2;t++){
        for(int k=1;k<=n;k++){
            for(int i=1;i<=n;i++){
                for(int j=1;j<=n;j++){
                    f[t][i][j]=min(f[t][i][k]+f[t][k][j],f[t][i][j]);
                }
            }
        }
    }
    
    for(int i=1;i<=n;i++){
        for(int j=1;j<=n;j++){
            g[i][j]=f[2][i][j];
            for(int k=1;k<=n;k++){
                g[i][j]=min(g[i][j],f[2][i][k]+f[1][k][j]+f[0][j][k]+f[1][k][j]);
                g[i][j]=min(g[i][j],f[1][i][k]+f[0][k][i]+f[1][i][k]+f[2][k][j]);
            }
        };
    }
    for(int k=1;k<=n;k++){
        for(int i=1;i<=n;i++){
            for(int j=1;j<=n;j++)g[i][j]=min(g[i][j],g[i][k]+g[k][j]);
        }
    }
    for(int i=1;i<=n;i++){
        for(int j=1;j<=n;j++){
            if(g[i][j]>=inf)g[i][j]=-1;
            cout<<g[i][j]<<' ';
        }cout<<'\n';
    }
    
}
int main(){
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    // int T;cin>>T;while(T--)
    Silverwolf();
    return 0;
}
/*
5 7
1 2 500 2
2 3 100 1
3 5 20 2
5 4 5 1
4 2 1 0
3 4 40 2
5 4 77 1
*/

J

题意简述

给定一个 \(h\times w\) 的 \(01\) 字符串,你可以执行以下三种操作:

  1. 将 \((i,j)\) 元素翻转
  2. 将 第 \(i\) 行的元素翻转
  3. 将 第 \(j\) 列的元素翻转

求经过一系列以上三种操作,以及尚未执行任何操作时,需要至少操作多少次,能使得矩阵变成全 0 矩阵。

思路简述

由于 \(h\leq 8\),那么显然,这是这道题的突破口。

对于每列的 \(01\) 串,可以视作一个 \(0\) 到 \(255\) 的一个二进制编码。

对于任意一列的这样的二进制编码(或 01 串),我的最小次数的操作步骤一定满足如下条件:

  1. 首先,我将每一行的这样的二进制编码,都将它的值优先变成 \(x\) 或 \(N\oplus x\)。其中 $\oplus $ 表示二进制下的异或符号,而 \(N=2^n -1\),显然 \(N\oplus x\) 表示的是 \(x\) 的反码。
  2. 假设我最终产生了 \(x\) 的数量为 \(cnt_x\),那么我需要先将这些 \(x\) 都取反变成 \(N\oplus x\),需要的贡献是 \(cnt_x\)。
  3. 最终,翻转行需要产生的贡献是 \(pop(N\oplus x)\),这表示这个数二进制下 1 的位数。

很好,对于没有任何修改的情况,我们就解决了。

我们可以记录一个东西 \(f_{i,j}\) 表示从 \(i\) 这个数变成 \(j\),再变成 \(j\oplus N\) 需要消耗的最小的贡献。

\(i\) 有两种途径变成 \(j\oplus N\),第一种途径是直接变,贡献是 \(pop(i\oplus (j\oplus N))\)

第二种途径是先变成 \(j\),再整体翻转,贡献是 \(pop(i\oplus j)+1\)

两者取最小值即可处理出 \(f_{i,j}\)。

难道每次询问我们都要枚举一下变成哪个数字 \(x\) 吗?

这不现实。

所以我们可以维护一个 \(dp_x\),表示将所有数变成 \(x\) 所需要的总消耗。既然你 \(f_{i,j}\) 都维护出来了,这样的一个东西也很好维护。

对于第 \(1,3\) 种修改,本质上就是将某个位置的掩码改变一下,因此我们可以删除原有掩码对 \([0,N]\) 所数的贡献,加上一个新的掩码对 \([0,N]\) 的贡献。

对于第 \(2\) 种修改,本质上是对每个掩码都映射到了新的掩码,我们可以将 \(dp[x]\) 的值存到 \(dp[x\oplus 2^i]\) 的位置即可了。

综上所述,我们可以在 \((2^h(2^h +w+q))\) 的时间复杂度内解决这个问题。

代码实现

#include<bits/stdc++.h>
using ll=long long;
using namespace std;
const int N=404;
const ll inf=1e9+7;
void Silverwolf(){
    int n,m,q;
    cin>>n>>m>>q;
    int N=(1<<n)-1;
    vector<int>mask(m+1,0);
    vector<vector<char>>a(n+1,vector<char>(m+1,0));
    vector<int>cnt(N+1,0);
    for(int i=0;i<n;i++){
        for(int j=1;j<=m;j++){
            cin>>a[i][j];
            int ch=a[i][j]-'0';
            mask[j]+=(1<<i)*ch;
            
        }
    }
    for(int j=1;j<=m;j++){
        cnt[mask[j]]++;
    }
    vector<vector<int>>f(N+1,vector<int>(N+1,0));
    for(int i=0;i<=N;i++){
        for(int j=0;j<=N;j++){
            f[i][j]=min(__builtin_popcount(i^j)+1,__builtin_popcount(i^j^N));
        }
    }
    vector<int>dp(N+1,0);
    for(int i=0;i<=N;i++){
        for(int j=0;j<=N;j++){
            dp[i]+=cnt[j]*f[j][i];
        }
    }
    auto get_ans=[&]()->int{
        int ans=inf;
        for(int i=0;i<=N;i++){
            ans=min(ans,dp[i]+__builtin_popcount(i^N));
        }
        return ans;
    };
    
    cout<<get_ans()<<'\n';
    auto add=[&](int x,int tg,int cnt)->void{
        for(int i=0;i<=N;i++){
            dp[i]+=cnt*tg*f[x][i];
        }
    };
    int tot=0;
    while(q--){
        char ch;
        cin>>ch;
        if(ch=='P'){
            int i,j;
            cin>>i>>j;
            --i;
            add(tot^mask[j],-1,1);
            cnt[tot^mask[j]]--;
            mask[j]^=(1<<i);
            cnt[tot^mask[j]]++;
            add(tot^mask[j],1,1);
        }else if(ch=='K'){
            int j;
            cin>>j;
            add(tot^mask[j],-1,1);
            cnt[tot^mask[j]]--;
            mask[j]^=N;
            cnt[tot^mask[j]]++;
            add(tot^mask[j],1,1);
        }else{
            int i;
            cin>>i;
            //第 i 行
            --i;
            tot^=(1<<i);
            vector<int>ncnt(N+1,0);
            vector<int>ndp(N+1,0);
            for(int j=0;j<=N;j++){
                ncnt[j^(1<<i)]+=cnt[j];
                ndp[j^(1<<i)]=dp[j];
            }
            cnt=ncnt;
            dp=ndp;
        }
        cout<<get_ans()<<'\n';
    }
}
int main(){
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    // int T;cin>>T;while(T--)
    Silverwolf();
    return 0;
}
/*
3 4 6
1010
1101
0010
R 2
P 3 1
K 2
P 2 1
K 4
P 3 4
*/

导航