【Educational Codeforces Round 54 (Rated for Div. 2) 】

CodeForces竞赛解析

前言

这场edu还算打的比较顺利,好久没打cf的我前期竟然比以前还稳这场edu还算打的比较顺利,好久没打cf的我前期竟然比以前还稳educf
稳地莫名其妙,果然第四题栽住了稳地莫名其妙,果然第四题栽住了
看到题之后自己脑补了一颗dij跑出来的树(赛后才知道这叫最短路树)看到题之后自己脑补了一颗dij跑出来的树(赛后才知道这叫最短路树)dij
好久不敲图论自己上去硬莽,写了一个多小时才艰难通过好久不敲图论自己上去硬莽,写了一个多小时才艰难通过
导致第五题没时间看,可能看了场上也敲不出来导致第五题没时间看,可能看了场上也敲不出来
rating+12,距离上紫还有一些时日,codeforces太好玩了rating+12,距离上紫还有一些时日,codeforces太好玩了rating+12,codeforces


A. Diverse Substring

题意

从一个长度为n的字符串中移除一个字符使整个字符串字典序变得最小从一个长度为n的字符串中移除一个字符使整个字符串字典序变得最小n使
1&lt;=n&lt;=2∗1051&lt;=n&lt;=2*10^51<=n<=2105

做法

如果可以让字典序变小,而且保证最小,肯定是优先移除前面的,不然移除后面的字典序还是不变如果可以让字典序变小,而且保证最小,肯定是优先移除前面的,不然移除后面的字典序还是不变
所以就找到第一个移除之后能使字典序变小的字符一处就可以所以就找到第一个移除之后能使字典序变小的字符一处就可以使
什么样的字符移除后能使字典序变小呢什么样的字符移除后能使字典序变小呢使
思考一下就是str[i]&gt;str[i+1]的时候,移除str[i]会使字典序变小思考一下就是str[i]&gt;str[i+1]的时候,移除str[i]会使字典序变小str[i]>str[i+1]str[i]使

坑点

注意原字符串一直递增时删掉最后一个最优注意原字符串一直递增时删掉最后一个最优

代码

#include<iostream>
#include<math.h>
#include<stdio.h>
#include<algorithm>
#include<queue>
#include<map>
#include<bitset>
#include<stack>
#include<set>
#include<vector>
#include <time.h>
#include<string.h>
using namespace std;

typedef long long ll;
typedef unsigned long long ull;
typedef double db;
typedef pair <int, int> pii;
typedef pair <ll, ll> pll;
typedef pair <ll, int> pli;
typedef pair <db, db> pdd;

const int maxn = 2e5+5;
const int Mod=1000000007;
const int INF = 0x3f3f3f3f;
const ll LL_INF = 0x3f3f3f3f3f3f3f3f;
const double e=exp(1);
const db PI = acos(-1);
const db ERR = 1e-10;

#define Se second
#define Fi first
#define pb push_back
#define dbg(x) cout<<#x<<" = "<< (x)<< endl
#define dbg2(x1,x2) cout<<#x1<<" = "<<x1<<" "<<#x2<<" = "<<x2<<endl
#define dbg3(x1,x2,x3) cout<<#x1<<" = "<<x1<<" "<<#x2<<" = "<<x2<<" "<<#x3<<" = "<<x3<<endl

char str[maxn];
int a[maxn];
int main()
{
    //ios::sync_with_stdio(false);
    //freopen("a.txt","r",stdin);
    //freopen("b.txt","w",stdout);
    int len;
    scanf("%d%s",&len,str);
    for(int i=0;i<len-1;i++)
    {
        if(str[i]>str[i+1])
        {
            for(int j=0;j<i;j++) printf("%c",str[j]);
            for(int j=i+1;j<len;j++) printf("%c",str[j]);
            return 0;
        }
    }
    for(int i=0;i<len-1;i++)
    {
         printf("%c",str[i]);
    }
    //cout << "time: " << (long long)clock() * 1000 / CLOCKS_PER_SEC << " ms" << endl;
    return 0;
}

B. Divisor Subtraction

题意

不断执行从n中减去n的最小质因子的过程不断执行从n中减去n的最小质因子的过程nn
求这个过程会被执行多少次求这个过程会被执行多少次
2&lt;=n&lt;=10102&lt;=n&lt;=10^102<=n<=1010

做法

若n为偶数,毫无疑问每次都−2,次数就是n/2若n为偶数,毫无疑问每次都-2,次数就是n/2n2n/2
若n为奇数,n一定存在一个质因子,而这个质因子一定是奇数若n为奇数,n一定存在一个质因子,而这个质因子一定是奇数nn
n减去这个质因子之后又变为偶数,再/2+1就是答案n减去这个质因子之后又变为偶数,再/2+1就是答案n/2+1

坑点

注意n本来就是质数时,直接输出1即可注意n本来就是质数时,直接输出1即可n1
还要注意for循环中的i要开longlong还要注意for循环中的i要开long longforilonglong

代码

#include<iostream>
#include<math.h>
#include<stdio.h>
#include<algorithm>
#include<queue>
#include<map>
#include<bitset>
#include<stack>
#include<set>
#include<vector>
#include <time.h>
#include<string.h>
using namespace std;

typedef long long ll;
typedef unsigned long long ull;
typedef double db;
typedef pair <int, int> pii;
typedef pair <ll, ll> pll;
typedef pair <ll, int> pli;
typedef pair <db, db> pdd;

const int maxn = 2e5+5;
const int Mod=1000000007;
const int INF = 0x3f3f3f3f;
const ll LL_INF = 0x3f3f3f3f3f3f3f3f;
const double e=exp(1);
const db PI = acos(-1);
const db ERR = 1e-10;

#define Se second
#define Fi first
#define pb push_back
#define dbg(x) cout<<#x<<" = "<< (x)<< endl
#define dbg2(x1,x2) cout<<#x1<<" = "<<x1<<" "<<#x2<<" = "<<x2<<endl
#define dbg3(x1,x2,x3) cout<<#x1<<" = "<<x1<<" "<<#x2<<" = "<<x2<<" "<<#x3<<" = "<<x3<<endl
#define maxn 100005
int main()
{
    ll n;
    ll ans=0;
    scanf("%lld",&n);
    if(n%2==0)
    {
        printf("%lld\n",n/2);
        return 0;
    }
    for(ll i=3;i*i<=n;i++)
    {
        if(n%i==0)
        {
            printf("%lld\n",(n-i)/2+1);
            return 0;
        }
    }
    printf("1\n");//n为质数的情况
    //cout << "time: " << (long long)clock() * 1000 / CLOCKS_PER_SEC << " ms" << endl;
    return 0;
}

C. Meme Problem

题意

a+b=d a∗b=da+b=d \ a*b=da+b=d ab=d
求a,b是否存在,若存在则输出求a,b是否存在,若存在则输出a,b

做法

初中数学题,不明白为什么被放在c题初中数学题,不明白为什么被放在c题c

代码

#include<iostream>
#include<math.h>
#include<stdio.h>
#include<algorithm>
#include<queue>
#include<map>
#include<bitset>
#include<stack>
#include<set>
#include<vector>
#include <time.h>
#include<string.h>
using namespace std;

typedef long long ll;
typedef unsigned long long ull;
typedef double db;
typedef pair <int, int> pii;
typedef pair <ll, ll> pll;
typedef pair <ll, int> pli;
typedef pair <db, db> pdd;

const int maxn = 2e5+5;
const int Mod=1000000007;
const int INF = 0x3f3f3f3f;
const ll LL_INF = 0x3f3f3f3f3f3f3f3f;
const double e=exp(1);
const db PI = acos(-1);
const db ERR = 1e-10;

#define Se second
#define Fi first
#define pb push_back
#define dbg(x) cout<<#x<<" = "<< (x)<< endl
#define dbg2(x1,x2) cout<<#x1<<" = "<<x1<<" "<<#x2<<" = "<<x2<<endl
#define dbg3(x1,x2,x3) cout<<#x1<<" = "<<x1<<" "<<#x2<<" = "<<x2<<" "<<#x3<<" = "<<x3<<endl
#define maxn 100005
int main()
{
    int t;
    scanf("%d",&t);
    while(t--)
    {
        double x_1,x_2,d;
        scanf("%lf",&d);
        if(d*d-4*d<0)
        {
            printf("N\n");
            continue;
        }
        printf("Y\n");
        x_1=-0.5*(-d+sqrt(d*d-4*d));
        x_2=-0.5*(-d-sqrt(d*d-4*d));
        printf("%.10f %.10f\n",x_1,x_2);
    }
    //cout << "time: " << (long long)clock() * 1000 / CLOCKS_PER_SEC << " ms" << endl;
    return 0;
}

D. Edge Deletion

题意

在一个n个点m条边的无向图中起点为1,设初始到达第i个点的最短距离为d[i]在一个n个点m条边的无向图中起点为1,设初始到达第i个点的最短距离为d[i]nm1id[i]
现在要求在图上删边,使剩下的边不超过k条,并让尽量多的点d[i]与之前相等现在要求在图上删边,使剩下的边不超过k条,并让尽量多的点d[i]与之前相等使kd[i]

做法

我们发现dij跑出来的图最终每个点只有一个前驱我们发现dij跑出来的图最终每个点只有一个前驱dij
那么这就是一棵树,在树上从根节点往下保存边不会影响子孙的d那么这就是一棵树,在树上从根节点往下保存边不会影响子孙的dd
所以跑出dij树之后从根往下保留k条边就可以了。所以跑出dij树之后从根往下保留k条边就可以了。dijk

坑点

最短路用dij不要用spfa最短路用dij不要用spfadijspfa
边权和会超过long long边权和会超过long \ longlong long

代码

#include<iostream>
#include<math.h>
#include<stdio.h>
#include<algorithm>
#include<queue>
#include<map>
#include<bitset>
#include<stack>
#include<set>
#include<vector>
#include <time.h>
#include<string.h>
using namespace std;

typedef long long ll;
typedef unsigned long long ull;
typedef double db;
typedef pair <int, int> pii;
typedef pair <ll, ll> pll;
typedef pair <ll, int> pli;
typedef pair <db, db> pdd;

const int maxn = 3e5+5;
const int Mod=1000000007;
const int INF = 0x3f3f3f3f;
const ll LL_INF = 0x3f3f3f3f3f3f3f3f;
const double e=exp(1);
const db PI = acos(-1);
const db ERR = 1e-10;

#define Se second
#define Fi first
#define pb push_back
#define dbg(x) cout<<#x<<" = "<< (x)<< endl
#define dbg2(x1,x2) cout<<#x1<<" = "<<x1<<" "<<#x2<<" = "<<x2<<endl
#define dbg3(x1,x2,x3) cout<<#x1<<" = "<<x1<<" "<<#x2<<" = "<<x2<<" "<<#x3<<" = "<<x3<<endl

typedef long long ll;
#define maxm 600005
#define maxn 300005
#define inf 0x3f3f3f3f3f3f3f3f
struct P
{
    int to;
    ll cost;
    bool operator < (const P & a) const
    {
        return cost>a.cost;
    }
};
struct node
{
    int to;
    ll val;
    int nxt;
    int id;
    int s;
}edge[maxm];
int head[maxn],tot;//head和tot记得重置,head重置为-1
int n,m;//点数,边数,不要再main里面重新定义
bool vis[maxn];//每次在dij里面初始化为0
ll dis[maxn];//根据题意初始化为inf可能int可能longlong
void addedge(int x,int y,ll val,int id)
{
    edge[tot].to=y;
    edge[tot].val=val;
    edge[tot].nxt=head[x];
    edge[tot].id=id;
    head[x]=tot++;
}
int pre[maxn],prem[maxn];
int vv[maxn];
void Dijkstra(int s)
{
    memset(vis,0,sizeof(vis));
    fill(dis,dis+n+2,inf);
    dis[s]=0;
    priority_queue<P>q;
    q.push(P{s,0});
    while(!q.empty())
    {
        P p1=q.top();q.pop();
        int u=p1.to;
        if(vis[u])continue;
        vis[u]=1;
        for(int i=head[u];i+1;i=edge[i].nxt)
        {
            int v=edge[i].to;
            if(vis[v])continue;
            if(dis[v]>dis[u]+edge[i].val)
            {
                pre[v]=u;
                prem[v]=edge[i].id;
                dis[v]=dis[u]+edge[i].val;
                q.push(P{v,dis[v]});
            }
        }
    }
}
vector<pii> G[maxn];
vector<int> ans;
int ind[maxn];
int cc,k;
void dfs(int st)
{
    if(cc==k) return ;
    for(int i=0;i<G[st].size();i++)
    {
        cc++;
        ans.push_back(G[st][i].Se);
        if(cc==k) return;
        dfs(G[st][i].Fi);
        if(cc==k) return ;
    }
}
int main()
{
   int u,v;
   ll w;
   memset(head,-1,sizeof(head));
   scanf("%d%d%d",&n,&m,&k);
   for(int i=1;i<=m;i++)
   {
        scanf("%d%d%lld",&u,&v,&w);
        addedge(u,v,w,i);
        addedge(v,u,w,i);
   }
   Dijkstra(1);
   if(k>=n-1)
   {
       printf("%d\n",n-1);
       for(int i=2;i<=n;i++)
       {
           printf("%d ",prem[i]);
       }
   }
   else
   {
       int re=n-1;
       for(int i=2;i<=n;i++)
       {
          G[pre[i]].push_back(pii(i,prem[i]));
       }
        cc=0;
        dfs(1);
        printf("%d\n",k);
        for(int i=0;i<ans.size();i++)
        {
            printf("%d ",ans[i]);
        }
   }
    //cout << "time: " << (long long)clock() * 1000 / CLOCKS_PER_SEC << " ms" << endl;
    return 0;
}

E. Vasya and a Tree

题意

给你一颗n个点的树,每个点的权值最初为0给你一颗n个点的树,每个点的权值最初为0n0
有m个操作,每次操作有三个变量v,d,x有m个操作,每次操作有三个变量v,d,xmv,d,x
操作为在v的距离v&lt;=d的子树内所有节点权值+x操作为在v的距离v&lt;=d的子树内所有节点权值+xvv<=d+x
最终统计树上每个点的权值最终统计树上每个点的权值

做法

首先要明确两件事情首先要明确两件事情
性质1.每个人的操作只会影响到他的子孙(包括自己)性质1.每个人的操作只会影响到他的子孙(包括自己)1.()
性质2.每个人只会被他祖先的操作所影响(包括自己)性质2.每个人只会被他祖先的操作所影响(包括自己)2.()
也就是说,如果我们能在访问到某个节点时,统计出所有影响到该节点的祖先操作也就是说,如果我们能在访问到某个节点时,统计出所有影响到该节点的祖先操作访
就可以统计出这个节点的最终权值就可以统计出这个节点的最终权值
而对于每个操作,我们只要用一个dep数组保存每个深度被增加的值而对于每个操作,我们只要用一个dep数组保存每个深度被增加的值dep
所有深度大于当前节点的操作都会影响到当前节点,如果用线段树就是一个区间求和问题所有深度大于当前节点的操作都会影响到当前节点,如果用线段树就是一个区间求和问题线
为了减少代码量我们用树状数组,更新时只在本次操作的最深的深度更新为了减少代码量我们用树状数组,更新时只在本次操作的最深的深度更新
这样求一个1−maxdep的前缀和就是所有更新了根节点的操作这样求一个1-maxdep的前缀和就是所有更新了根节点的操作1maxdep
在求一个1−(nowdep−1)的前缀和就是所有不包含当前节点的操作在求一个1-(nowdep-1)的前缀和就是所有不包含当前节点的操作1(nowdep1)
两个前缀和相减就是当前节点被更新的值两个前缀和相减就是当前节点被更新的值
为了保证每个操作只影响自己子树内的节点,在dfs退出子树时为了保证每个操作只影响自己子树内的节点,在dfs退出子树时dfs退
要将当前根节点的所有修改值还原要将当前根节点的所有修改值还原

代码

#include<stdio.h>
#include<iostream>
#include<algorithm>
#include<vector>
using namespace std;
typedef long long ll;
const int maxn = 3e5+10;
int n,m;
ll tree[maxn],ans[maxn];
vector<int> G[maxn],D[maxn],X[maxn];
void add(int x,int val)
{
    while(x<=n)
    {
        tree[x]+=val;
        x=x+(x&-x);
    }
}
ll sum(int x)
{
    ll ans=0;
    while(x)
    {
        ans+=tree[x];
        x=x-(x&-x);
    }
    return ans;
}
void dfs(int x,int fa,int dep)
{
    for(int i=0;i<D[x].size();i++)
    {
        add(min(D[x][i]+dep,n),X[x][i]);//进子树之前更新
    }
    ans[x]=sum(n)-sum(dep-1);//树状数组变区间查询为两个前缀和相减
    //由于性质2,所以在这个地方就可以直接算出当前节点的最终答案
    for(int i=0;i<G[x].size();i++)
    {
        if(G[x][i]==fa) continue;
        dfs(G[x][i],x,dep+1);
    }
    for(int i=0;i<D[x].size();i++)
    {
        add(min(D[x][i]+dep,n),-X[x][i]);//出子树之后还原
    }
}
int main()
{
    int x,y,z;
    scanf("%d",&n);
    for(int i=1;i<=n-1;i++)
    {
        scanf("%d%d",&x,&y);
        G[x].push_back(y);
        G[y].push_back(x);
    }
    scanf("%d",&m);
    for(int i=1;i<=m;i++)
    {
        scanf("%d%d%d",&x,&y,&z);
        D[x].push_back(y);
        X[x].push_back(z);
    }
    dfs(1,0,1);
    for(int i=1;i<=n;i++)
    {
        printf("%lld ",ans[i]);
    }
    return 0;
}
"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级选手设计的教育性比赛,旨在提高他们的编程技能和算法能力。参与这样的比赛可以为选手提供学习和进步的机会,同时也促进了编程社区的交流与合作。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值