Codeforces Round #533 (Div. 2)题解 by_Hile

前言:本来在周报上夸下海口说一周补一套Div1,结果发现以前打过的好几场Div2都没补完,于是这几周打算先补了打过的Div2,并且写完题解。
(感觉以前好菜,div2只能出两题(虽然现在还是很菜


A.Salem and Sticks (1100)

题意:有 n   ( 1 n 1000 ) n\ (1\leq n \leq 1000) 个长度分别为 a i   ( 1 a i 100 ) a_i\ (1\leq a_i\leq 100) 的木棒,你可以花费 b i a i |b_i-a_i| 使一根木棒的长度从 a i a_i 变为 b i b_i ,求让所有木棒满足 b i t 1 |b_i-t|\leq1 的最小花费与此时的 t t

思路:由于 a i a_i 最大只有100,暴力枚举所有可能的 t t 并记录最小费用即可,时间复杂度 O ( 100 n ) O(100n)

代码

#include<bits/stdc++.h>
using namespace std;
int n,a[1010],ans=0x3f3f3f3f,tt;
int main()
{
    cin>>n;
    for(int i=1;i<=n;i++)
        cin>>a[i];
    for(int t=1;t<=100;t++)
    {
        int sum=0;
        for(int i=1;i<=n;i++)
            sum+=min(abs(a[i]-t),min(abs(a[i]-t+1),abs(a[i]-t-1)));
        if(ans>sum)
        {
            ans=sum;
            tt=t;
        }
    }
    cout<<tt<<" "<<ans;
}

B.Zuhair and Strings (1200)

题意:求出长度为 n   ( 1 n 2 e 5 ) n\ (1\leq n\leq 2e5) 的字符串中,不相交且长度为 k   ( 1 k n ) k\ (1\leq k\leq n) 的只含一种字母的子串个数。

思路:暴力记录所有符合条件的子串,然后找最大数量,时间复杂度 O ( n ) O(n)

代码

#include<bits/stdc++.h>
using namespace std;
int n,k,ans,mp[26];
string s;
int main()
{
    cin>>n>>k>>s;
    for(int i=0;i<s.size();i++)
    {
        int cnt=0;
        for(int j=i;j<i+k;j++)
        {
            if(s[i]!=s[j])
            {
                cnt=j-i;
                break;
            }
        }
        if(!cnt)
        {
            mp[s[i]-'a']++;
            cnt=k;
        }
        i+=cnt-1;
    }
    for(int i=0;i<26;i++)
        ans=max(ans,mp[i]);
    cout<<ans;
}

C.Ayoub and Lost Array (1500)

题意:存在一长度为 n   ( 1 n 2 e 5 ) n\ (1\leq n \leq 2e5) 的数组,其中每个元素的大小都属于 [ l , r ]   ( 1 l r 1 e 9 ) [l,r]\ (1\leq l\leq r \leq 1e9) ,求满足数组所有元素之和 m o d    3 = 0 \mod 3=0 的方案数(对 1 e 9 + 7 1e9+7 取模)。

思路线性dp,用 d p [ i ] [ j ] dp[i][j] 表示枚举到数组第 i i 位时和 m o d    3 = j \mod 3=j 的方案数(对 1 e 9 + 7 1e9+7 取模)。则状态转移方程为
d p [ i ] [ ( j + k ) % 3 ] = d p [ i ] [ ( j + k ) % 3 ] + d p [ i 1 ] [ k ] c n t j dp[i][(j+k)\%3]=dp[i][(j+k)\%3]+dp[i-1][k]*cnt_j
由于从 d p [ i 1 ] [ 0 : 2 ] dp[i-1][0:2] d p [ i ] [ 0 : 2 ] dp[i][0:2] 共有九种转移方法,上述方程表示从 k k 转移到 ( j + k ) % 3 (j+k)\%3
其中 c n t j cnt_j 为在 [ l , r ] [l,r] m o d    3 = j \mod 3=j 的方案数,为 r j + 3 3 l j + 2 3 \lfloor \frac{r-j+3}{3} \rfloor -\lfloor \frac{l-j+2}{3} \rfloor 。(关键结论
代码

#include<bits/stdc++.h>
#define ll long long
#define MOD 1000000007
using namespace std;
ll n,l,r,ans,dp[200010][3];
int main()
{
    cin>>n>>l>>r;
    dp[0][0]=1;
    for(int i=1;i<=n;i++)
    {
        for(int j=0;j<3;j++)
        {
            ll cnt=(r-j+3)/3-(l-j+2)/3;
            for(int k=0;k<3;k++)
                dp[i][(j+k)%3]=(dp[i][(j+k)%3]+dp[i-1][k]*cnt)%MOD;
        }
    }
    cout<<dp[n][0];
}

D.Kilani and the Game (1900)

题意:给定一个 n × m n\times m 的只由数字、’#‘和’.‘构成的图,数字 i i 代表 i i 号玩家的初始领地,共有 p p 名玩家,每回合从 1 1 号玩家开始进行 a [ i ] a[i] 次扩张, i i 号玩家每次扩张可以让图中所有 i i 号点四周的’.'变为 i i ,求出当整张图扩张完毕的时候所有数字的数量。

思路BFS,记录每回合开始的所有玩家的所有源点,依次往外扩张 a [ i ] a[i] 的距离后将停止时的点压入滚动队列使其成为下一轮的源点,然后重复上述过程直至所有队列都为空,表示游戏结束,时间复杂度 O ( n m p ) O(nmp)
Bonus:读题时想到的另一种算法,先进行 p p 次BFS预处理出所有每一点到每个玩家最近源点的曼哈顿距离(考虑’#’)并用一个 1000 1000 10 1000*1000*10 的三维数组储存, p p 点的编号即为距离最小的玩家的编号,遍历整张图后即可统计出答案。
(口头AC,算法并未实现,正确性和复杂度未知,但时空复杂度显然高于原算法
代码

#include<bits/stdc++.h>
using namespace std;
int n,m,p,a[10];//如题
int ans[10],rod=1;//ans[i]:第i号玩家的格子数,rod:当前进行的游戏轮数
int xy[4][2]={{1,0},{0,1},{-1,0},{0,-1}};//单位向量,便于bfs
bool vis[1010][1010];//当前点是否可行
string s[1010];//存图
struct node
{
    int x,y,s;//存坐标(x,y)和节点深度s
};
queue<node> qu[10][2];//qu[i][]表示i号玩家每一轮的起始节点,滚动一维
void bfs(int x)//bfs求x号玩家第rod轮的扩张情况
{
    while(!qu[x][rod&1].empty())
    {
        node tmp=qu[x][rod&1].front();
        qu[x][rod&1].pop();
        if(tmp.s>rod*a[x]&&!vis[tmp.x][tmp.y])
        {
            qu[x][(rod+1)&1].push(tmp);
            continue;
        }
        if(vis[tmp.x][tmp.y])continue;
        vis[tmp.x][tmp.y]=true;
        ans[x]++;
        for(int i=0;i<4;i++)
        {
            int tx=tmp.x+xy[i][0],ty=tmp.y+xy[i][1];
            if(!vis[tx][ty]&&s[tx][ty]=='.')
                qu[x][rod&1].push({tx,ty,tmp.s+1});
        }
    }
}
int main()
{
    cin>>n>>m>>p;
    for(int i=1;i<=p;i++)cin>>a[i];
    //存图,并在所给图的外围加一圈'#'
    for(int i=0;i<=m+1;i++)s[0].push_back('#'),vis[0][i]=1;
    for(int i=0;i<=m+1;i++)s[n+1].push_back('#'),vis[n+1][i]=1;
    for(int i=1;i<=n;i++)
    {
        s[i].push_back('#');vis[i][0]=1;
        string tmp;cin>>tmp;s[i]+=tmp;
        s[i].push_back('#');vis[i][m+1]=1;
    }
    for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++)
            if(isdigit(s[i][j]))
                qu[s[i][j]-'0'][rod&1].push({i,j,0});
            else if(s[i][j]=='#')
                vis[i][j]=1;
    while(true)
    {
        int flag=0;
        for(int i=1;i<=p;i++)
            bfs(i);
        rod++;
        for(int i=1;i<=p;i++)//若所有队列都为空说明无法扩张,游戏结束退出循环
            for(int j=0;j<2;j++)
                if(!qu[i][j].empty())
                    flag++;
        if(!flag)break;
    }
    for(int i=1;i<=p;i++)
        cout<<ans[i]<<" ";
    return 0;
}

E.Helping Hiasat (2200)

题意:未完待续

思路

代码


发布了7 篇原创文章 · 获赞 10 · 访问量 440

猜你喜欢

转载自blog.csdn.net/weixin_44700995/article/details/103006558