ABC375部分题解

关于C题卡了我队友一小时这件事

Posted by BingYu2016 on October 14, 2024

C - Spiral Rotation

难得给C一次排面……好像也不算难得,无所谓了

题干的描述很抽象,但这题只要理解了题意就会很好写,这题的意思其实是:

首先将整个数组顺时针90度旋转一圈,然后除了最外圈再旋转,然后除了最外两圈再旋转……直至旋转完最内圈。

如果暴力模拟全部操作无疑会超时。我们注意到,全部操作结束后,最外圈一共旋转了90度,从外向内第二圈旋转了180度,第三圈旋转了270度……因而,我们可以很轻易的得出每一圈的最终旋转角度,然后对其取模即可每圈只用一次操作解决问题。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
void solve() {
    int n;
    cin>>n;
    string s[n+2],ss[n+2];
    for(int i=1;i<=n;i++){
        cin>>s[i];
        s[i]='x'+s[i];
        ss[i]=s[i];
        //cout<<s[i]<<endl;
    }
    for(int i=1;i<=n;i++){
        for(int j=1;j<=n;j++){
            int cir=min(min(i,abs(i-n-1)),min(j,abs(j-n-1)));
            //cout<<cir;
            if(cir%4==0){
                ss[i][j]=s[i][j];
            }
            if(cir%4==1){
                ss[j][n+1-i]=s[i][j];
            }
            if(cir%4==2){
                ss[n+1-i][n+1-j]=s[i][j];
            }
            if(cir%4==3){
                ss[n+1-j][i]=s[i][j];
            }
        }
        //cout<<endl;
    }
    for(int i=1;i<=n;i++){
        for(int j=1;j<=n;j++){
            cout<<ss[i][j];
        }
        cout<<endl;
    }
}

D - ABA

我们考虑这样一种方法计算字符串中每一位的贡献:该位的贡献为该位与左侧所有同字符位之间间隔字符的数量和。

基于此,我们只需要对每一种字符存储三个变量:出现过的该字符数量和num,出现过的该字符下标和al,出现过的该字符的贡献总和sum。从左到右进行一次遍历,每到达一位,便通过$sum_i=num*(i-1)-al$这一公式计算该位的贡献并加到sum中,随后更新num和al,最后加和所有字符的sum便是最终答案。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
struct abc{
    ll sum=0,num=0,al=0;
};

void solve() {
    string s;
    cin>>s;
    map<char,abc> mp;
    for(int i=0;i<s.size();i++){
        if(mp.find(s[i])==mp.end()){
            mp[s[i]].al=i;
            mp[s[i]].num=1;
        }
        else{
            mp[s[i]].sum+=mp[s[i]].num*(i-1)-mp[s[i]].al;
            mp[s[i]].al+=i;
            mp[s[i]].num++;
        }
    }
    ll ans=0;
    for(auto x:mp){
        ans+=x.second.sum;
    }
    cout<<ans<<endl;
}

E - 3 Team Division

考虑到所有B的总和不超过1500,我们自然的想到可以利用DP来解题。

最基础的DP思路是$DP[N][A][B][C]$,四个状态分别为当前待安排人员与ABC三组的总战力,但我们易注意到这样会导致超时。考虑到符合要求的$A+B+C$的值是固定的且最终合规状态下ABC相等,我们可以省略掉对C的状态的枚举,从而将时间复杂度降低到可以接受的范围。状态转移方程的右式我们用以保存队员转移次数,注意因为我们省略了C的维度,所以在状态转移时要额外考虑队员转移到C队所带来的转移次数影响。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
void solve() {
    int n;
    cin>>n;

    int dp[600][600];
    memset(dp,0x3f,sizeof(dp));
    dp[0][0]=0;

    pair<int,int>peo[200];
    int sum=0;
    for(int i=0;i<n;i++){
    cin>>peo[i].first>>peo[i].second;
    sum+=peo[i].second;
    }
    //cout<<sum;
    for(int i=0;i<n;i++){
        for(int x=sum/3;x>=0;x--){
            for(int y=sum/3;y>=0;y--){
                dp[x][y]+=(peo[i].first!=3);
                if(x-peo[i].second>=0){
                    dp[x][y]=min(dp[x][y],dp[x-peo[i].second][y]+(peo[i].first!=1));
                }
                if(y-peo[i].second>=0){
                    dp[x][y]=min(dp[x][y],dp[x][y-peo[i].second]+(peo[i].first!=2));
                }
            }
        }
    }
    if(dp[sum/3][sum/3]>200||sum%3!=0)cout<<"-1";
    else cout<<dp[sum/3][sum/3];
}

啥也不是,散会(讲师音)

哦对讲师现在吃住条件比我好,这下难绷了