Educational Codeforces Round 161 div2(CF1922)题解

本文介绍了几个与IT技术相关的算法问题,包括字符串模板匹配、小木棍构成三角形的计数、计算最短路径费用、怪兽动态死亡模型和最长上升子序列生成,以及区间动态规划的优化
摘要由CSDN通过智能技术生成

A

给你一个整数 n n n 和三个字符串 a , b , c a, b, c a,b,c,每个字符串由 n n n 个小写字母组成。

假设一个模板是由 n n n 个字母组成的字符串 t t t。如果从 1 1 1 n n n 的所有 i i i 都满足以下条件,则字符串 s s s 与模板 t t t 匹配:

  • 如果模板中第 i i i 个字母是小写字母,那么 s i s_i si 必须与 t i t_i ti 相同
  • 如果模板中的第 i i i 个字母是小写字母,那么 s i s_i si 必须与 t i t_i ti小写版本不同。例如,如果模板中有字母 A,则不能在字符串的相应位置使用字母 a

因此,如果至少有一个 i i i 的条件不成立,字符串就与模板不匹配。

判断是否存在模板 t t t,使得字符串 a a a b b b 匹配,而字符串 c c c 不匹配。

只要至少有一个位置使得 c i ≠ a i 且 c i ≠ b i c_i \ne a_i 且 c_i \ne b_i ci=aici=bi即可

#include<bits/stdc++.h>
using namespace std;
#define rep(i,a,b) for(int i=a;i<=(b);++i)
#define trav(a,x) for(auto&a : x)
#define all(x) x.begin(),x.end()
#define sz(x) (int) x.size()
typedef pair<int,int> pii;
typedef vector<int> vi;
typedef long long ll;
string a,b,c;
int n;
void work()
{
    cin>>n;
    cin>>a>>b>>c;
    int tot=0;
    rep(i,0,n-1)
    {
        if(a[i]==b[i])
        {
            if(c[i]==a[i]) tot++;
        }
        else
        {
            if(a[i]==c[i]) tot++;
            if(b[i]==c[i]) tot++;
        }
    }
    if(tot==n) cout<<"NO\n";
    else cout<<"YES\n";
}
int main()
{
	cin.tie(0);
	cin.sync_with_stdio(0);
	int t=1;
	cin>>t;
	while(t--)
		work();
	return 0;
}

B

给定 n n n个长度为 2 a i 2^{a_i} 2ai的小木棍,问组成三角形有多少种方法

显然只有等边和等腰两种情况,分开计数即可

#include<bits/stdc++.h>
using namespace std;
#define rep(i,a,b) for(int i=a;i<=(b);++i)
#define trav(a,x) for(auto&a : x)
#define all(x) x.begin(),x.end()
#define sz(x) (int) x.size()
typedef pair<int,int> pii;
typedef vector<int> vi;
typedef long long ll;
map<int,int> mp;
ll cal2(int n)
{
    return (ll)n*(n-1)/2;
}
ll cal3(int n)
{
    return (ll)n*(n-1)*(n-2)/6;
}
void work()
{
    int n;
    cin>>n;
    rep(i,1,n)
    {
        int x;cin>>x;
        mp[x]++;
    }
    int sum=0;
    ll ans=0;
    trav(item,mp)
    {
        ans+=cal3(item.second);
        ans+=cal2(item.second)*sum;
        sum+=item.second;
    }
    cout<<ans<<'\n';
    mp.clear();
}
int main()
{
	cin.tie(0);
	cin.sync_with_stdio(0);
	int t=1;
	cin>>t;
	while(t--)
		work();
	return 0;
}

C

给定数轴上 n n n个城市的坐标,去最近的城市只要花费 1 1 1枚硬币,去其他城市需要花费路程长度枚硬币,数据保证每个城市的最近城市唯一

m m m次询问 x x x y y y城市的最小花费

容易发现能走花费为 1 1 1的边就一定走,花费不能为 1 1 1就只能花费,于是问题转化成前缀和

n < = 1 e 5 n<=1e5 n<=1e5

#include<bits/stdc++.h>
using namespace std;
#define rep(i,a,b) for(int i=a;i<=(b);++i)
#define trav(a,x) for(auto&a : x)
#define all(x) x.begin(),x.end()
#define sz(x) (int) x.size()
typedef pair<int,int> pii;
typedef vector<int> vi;
typedef long long ll;
const int maxn=1e5+5;
int n;
int l[maxn],r[maxn],a[maxn];
ll suml[maxn],sumr[maxn];
void work()
{
    cin>>n;
    sumr[0]=suml[0]=0;
    rep(i,1,n)
        cin>>a[i];
    rep(i,1,n)
    {
        if(i==1) {r[i]=1;continue;}
        if(i==n) {l[i]=1;continue;}
        if(a[i]-a[i-1]>a[i+1]-a[i])
            r[i]=1,l[i]=a[i]-a[i-1];
        else
            l[i]=1,r[i]=a[i+1]-a[i];
    }
    rep(i,1,n)
        sumr[i]=sumr[i-1]+r[i],suml[i]=suml[i-1]+l[i];
    int q;
    cin>>q;
    while(q--)
    {
        int x,y;
        cin>>x>>y;
        if(x<y)
            cout<<sumr[y-1]-sumr[x-1]<<'\n';
        else
            cout<<suml[x]-suml[y]<<'\n';
    }
}
int main()
{
	cin.tie(0);
	cin.sync_with_stdio(0);
	int t=1;
	cin>>t;
	while(t--)
		work();
	return 0;
}

D

n n n只怪兽,给定每只怪兽的攻击力和防御力 a i , d i a_i,d_i ai,di

n n n轮,每轮每只怪兽会受到相邻两只怪兽的攻击,如果攻击力之和大于其防御力则死亡

输出这 n n n轮中每一轮死亡的怪兽数量

n < = 3 e 5 n<=3e5 n<=3e5

需要证明一下复杂度

如果怪兽在上一轮没有死亡,那么,只有当它上一轮相邻的两只中有一只死亡,我们才需要进行判断

每只怪兽至多被删除一次,故时间复杂度 O ( n ) O(n) O(n)

用链表维护即可

#include<bits/stdc++.h>
using namespace std;
#define rep(i,a,b) for(int i=a;i<=(b);++i)
#define trav(a,x) for(auto&a : x)
#define all(x) x.begin(),x.end()
#define sz(x) (int) x.size()
typedef pair<int,int> pii;
typedef vector<int> vi;
typedef long long ll;
const int maxn=3e5+5;
int pre[maxn],nxt[maxn],a[maxn],d[maxn];
bool vis[maxn],del[maxn];
int n;
queue<int> s,s1;
void work()
{
    cin>>n;
    rep(i,1,n) cin>>a[i];
    rep(i,1,n) cin>>d[i];
    a[n+1]=d[n+1]=0;
    rep(i,1,n) pre[i]=i-1,nxt[i]=i+1,s.push(i);
    nxt[0]=1;
    rep(i,1,n)
    {
        int res=0;

        while(!s.empty())
        {
            int pos=s.front();s.pop();
            if(vis[pos]) continue;
            if(a[pre[pos]]+a[nxt[pos]]>d[pos]) res++,s1.push(pos),del[pos]=true;
            vis[pos]=true;
        }

        while(!s1.empty())
        {
            int pos=s1.front();s1.pop();
            //printf("pos=%d\n",pos);
            if(pre[pos]&&!del[pre[pos]]) s.push(pre[pos]),vis[pre[pos]]=false;
            if(nxt[pos]!=n+1&&!del[nxt[pos]]) s.push(nxt[pos]),vis[nxt[pos]]=false;
            pre[nxt[pos]]=pre[pos];
            nxt[pre[pos]]=nxt[pos];
        }
        cout<<res<<' ';
    }
    cout<<'\n';
    rep(i,1,n) vis[i]=false,del[i]=false;
    //while(!s1.empty()) s1.pop();
    //while(!s.empty()) s.pop();
}
int main()
{
	cin.tie(0);
	cin.sync_with_stdio(0);
	int t=1;
	cin>>t;
	while(t--)
		work();
	return 0;
}

E

给定一个 X X X,构造一个长度不超过 200 200 200的恰好有 X X X个最长上升子序列的序列(其中空序列算一个)

X < = 1 e 18 X<=1e18 X<=1e18

首先需要发现一个长度为 n n n的单调递增的序列恰好有 2 n − 1 2^n-1 2n1个最长上升子序列

2 n − 1 2^n-1 2n1去凑 X X X会超过200的限制

想到向中间插入几个较大值,假设插入的位置为 x x x,就刚好有 2 x 2^x 2x的贡献,那么本题就解决了

#include<bits/stdc++.h>
using namespace std;
#define rep(i,a,b) for(int i=a;i<=(b);++i)
#define trav(a,x) for(auto&a : x)
#define all(x) x.begin(),x.end()
#define sz(x) (int) x.size()
typedef pair<int,int> pii;
typedef vector<int> vi;
typedef long long ll;
const int inf =1e8+5;
void work()
{
    ll x;
    cin>>x;
    ll tmp=x;
    int n=0;
    while(tmp)
    {
        tmp>>=1ll;n++;
    }

    int ans=n-1;
    rep(i,0,n-2)
        if(x>>i&1) ans++;

    cout<<ans<<'\n';

    rep(i,0,n-1)
    {
        if(i) cout<<i<<' ';
        if(x>>i&1&&i!=n-1) cout<<inf<<' ';
    }
    cout<<'\n';
}
int main()
{
	cin.tie(0);
	cin.sync_with_stdio(0);
	int t=1;
	cin>>t;
	while(t--)
		work();
	return 0;
}

F

给定长度为 n n n的序列 a n a_n an,值域为 [ 1 , x ] [1,x] [1,x]

每次可以选定一段区间 [ l , r ] [l,r] [l,r],和这段区间内不包含的一个数 k ( k ≤ x ) k(k \le x) k(kx),将区间中所有的数全都变成 k k k

问最少几次操作可以使得序列 a a a全部相等

x , n ≤ 500 x,n \le 500 x,n500

区间DP

f [ l ] [ r ] [ k ] f[l][r][k] f[l][r][k]为将区间 [ l , r ] [l,r] [l,r]变为 k k k的最小代价, g [ l ] [ r ] [ k ] g[l][r][k] g[l][r][k]表示将区间 [ l , r ] [l,r] [l,r]变为不包含 k k k的最小代价

那么有 f [ l ] [ r ] [ k ] = m i n ( f [ l ] [ i ] [ k ] + f [ i + 1 ] [ r ] [ k ] , g [ l ] [ r ] [ k ] + 1 ) f[l][r][k]=min(f[l][i][k]+f[i+1][r][k],g[l][r][k]+1) f[l][r][k]=min(f[l][i][k]+f[i+1][r][k],g[l][r][k]+1)

g [ l ] [ r ] [ k ] = m i n ( g [ l ] [ i ] [ k ] + g [ i + 1 ] [ r ] [ k ] ) , 且 g [ l ] [ r ] [ k ] = m i n ( g [ l ] [ r ] [ i ] + 1 ) ( i ≠ k ) g[l][r][k]=min(g[l][i][k]+g[i+1][r][k]),且g[l][r][k]=min(g[l][r][i]+1)(i \ne k) g[l][r][k]=min(g[l][i][k]+g[i+1][r][k]),g[l][r][k]=min(g[l][r][i]+1)(i=k)

#include<bits/stdc++.h>
using namespace std;
#define rep(i,a,b) for(int i=a;i<=(b);++i)
#define trav(a,x) for(auto&a : x)
#define all(x) x.begin(),x.end()
#define sz(x) (int) x.size()
typedef pair<int,int> pii;
typedef vector<int> vi;
typedef long long ll;
const int inf=1e8;
const int maxn=105;
int f[maxn][maxn][maxn],g[maxn][maxn][maxn],a[maxn];
int n,x;
void work()
{
    cin>>n>>x;
    rep(i,1,n)
        rep(j,1,n)
            rep(k,1,x)
                f[i][j][k]=g[i][j][k]=inf;
    rep(i,1,n)
    {
        cin>>a[i];
        rep(j,1,x)
            f[i][i][j]=(a[i]!=j),
            g[i][i][j]=(a[i]==j);
    }

    rep(len,1,n)
        rep(l,1,n-len+1)
        {
            int r=l+len-1,minn=inf;
            rep(k,1,x)
            {
                rep(i,l,r-1)
                    g[l][r][k]=min(g[l][i][k]+g[i+1][r][k],g[l][r][k]);
                minn=min(minn,g[l][r][k]);
            }
            rep(k,1,x)
                g[l][r][k]=min(g[l][r][k],minn+1);

            rep(k,1,x)
                rep(i,l,r-1)
                    f[l][r][k]=min(f[l][i][k]+f[i+1][r][k],min(g[l][r][k]+1,f[l][r][k]));
        }
    int ans=inf;
    rep(i,1,x)
        ans=min(ans,f[1][n][i]);
    cout<<ans<<'\n';
}
int main()
{
	cin.tie(0);
	cin.sync_with_stdio(0);
	int t=1;
	cin>>t;
	while(t--)
		work();
	return 0;
}

  • 17
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
"educational codeforces round 103 (rated for div. 2)"是一个Codeforces平台上的教育性比赛,专为2级选手设计评级。以下是有关该比赛的回答。 "educational codeforces round 103 (rated for div. 2)"是一场Codeforces平台上的教育性比赛。Codeforces是一个为程序员提供竞赛和评级的在线平台。这场比赛是专为2级选手设计的,这意味着它适合那些在算法数据结构方面已经积累了一定经验的选手参与。 与其他Codeforces比赛一样,这场比赛将由多个问题组成,选手需要根据给定的问题描述和测试用例,编写程序来解决这些问题。比赛的时限通常有两到三个小时,选手需要在规定的时间内提交他们的解答。他们的程序将在Codeforces的在线评测系统上运行,并根据程序的正确性和效率进行评分。 该比赛被称为"educational",意味着比赛的目的是教育性的,而不是针对专业的竞争性。这种教育性比赛为选手提供了一个学习和提高他们编程技能的机会。即使选手没有在比赛中获得很高的排名,他们也可以从其他选手的解决方案中学习,并通过参与讨论获得更多的知识。 参加"educational codeforces round 103 (rated for div. 2)"对于2级选手来说是很有意义的。他们可以通过解决难度适中的问题来测试和巩固他们的算法和编程技巧。另外,这种比赛对于提高解决问题能力,锻炼思维和提高团队合作能力也是非常有帮助的。 总的来说,"educational codeforces round 103 (rated for div. 2)"是一场为2级选手设计的教育性比赛,旨在提高他们的编程技能和算法能力。参与这样的比赛可以为选手提供学习和进步的机会,同时也促进了编程社区的交流与合作。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值