Atcoder abc 233 题解

Atcoder abc 233 题解

A 10yen Stamp

答案就是两个数字的差/10之后上取整
记得判断res=0的情况就可以了

c++代码

#include<iostream>
#include<algorithm>
#include<cstring>
#include<cmath>

using namespace std;

int main()
{
    int x,y;
    scanf("%d %d",&x,&y);
    if(x>=y)
    {
        puts("0");
        return 0;
    }
    else
    {
        int t=ceil(1.0*(y-x)/10);
        cout<<t<<endl;
        return 0;
    }
}

B - A Reverse

题意

第一行输入一个 L L L, R R R
第二行输入一个字符串 S S S
输出将字符串 S S S 的第 L L L R R R的子串

c++代码

#include<iostream>
#include<cstring>
#include<algorithm>

using namespace std;

const int N=1e5+10;
char s[N];

int main()
{
    int l,r;
    cin>>l>>r;
    cin>>s+1;
    string res;
    int n=strlen(s+1);
    int mid=(l+r)>>1;
    for(int i=1;i<=n;i++)
    {
        if(i>=l&&i<=mid) swap(s[i],s[l+r-i]);
    }
    cout<<(s+1)<<endl;
}

C - Product

题意

N N N个背包, 背包 i i i里面装了 L i L_i Li个球, 每个球的的质量为 a i , j a_i,j ai,j, 问有多少种方式可以在每个包里面选择一个球使选择的球的质量的乘积为 N N N

思路

根据题目给的数据范围可以知道所有包中的球数量的乘积最多是 1 0 5 10^5 105,所以这一题用dfs
dfs的参数:选择到了第几组,现在的乘积是多少
剪枝:当乘积大于 X X X需要剪枝
注意:需要开ll 要不然会爆掉

c++代码

#include<iostream>
#include<cstring>
#include<algorithm>
#include<vector>
#include<map>
#include<unordered_map>

using namespace std;

typedef long long ll;


const int N=1e5+10;
vector<ll> a[N];
ll sum;
int n;
int res=0;

void dfs(ll pos,ll now)//选择到了第几个背包 现在背包的重量是多少
{
    if(pos==n)//递归出口 全部处理完
    {
        if(now==sum) res++;
        return ;
    }
    for(int i=0;i<a[pos].size();i++)//枚举这个背包里面选择哪一个
    {
        if((ll)a[pos][i]*now>sum) continue;//剪枝
        else 
        {
            dfs(pos+1,now*a[pos][i]);
        }
    }
}

int main()
{
    cin>>n>>sum;
    for(int i=0;i<n;i++)
    {
        int s;
        scanf("%d",&s);
        for(int j=0;j<s;j++)
        {
            int x;
            scanf("%d",&x);
            a[i].push_back(x);
        }
    }
    dfs(0,1);
    cout<<res<<endl;
}

D - Count Interval

题意

给一个数组 A A A和一个整数 K K K,计算有多少个 ( ( ( L L L, R R R ) ) )满足数组A的[L,R]的和为K

思路

这是一道很简单的前缀和问题
设Sum[]是数组A的前缀和数组,数组A在[L,R]的和就是Sum[r]-Sum[L-1]
那么在这里我们只需要用map记录一下前缀和出现的次数然后进行计算就可以啦
注意:记得开long long 初始化mp[0]=1(选择的L为1的时候)

c++代码

#include<iostream>
#include<algorithm>
#include<cstring>
#include<unordered_map>

using namespace std;
typedef long long ll;

const int N=2e5+10;

ll a[N];
ll k;
int n;
unordered_map<ll,int> mp;

int main()
{
    cin>>n>>k;
    mp[0]=1;
    ll sum=0;
    ll res=0;
    for(int i=1;i<=n;i++)
    {
        ll x;
        scanf("%lld",&x);
        sum+=x;
        res+=mp[sum-k];
        mp[sum]++;
    }
    cout<<res<<endl;
}

E - Σ[k=0…10100]floor(X/10k)

题意

计算题目所给出式子的值

思路

手动模拟一下可以得到这个

 123456
+ 12345.6
+  1234.56
+   123.3456
+    12.3456
+     1.3456
--------------
=137171

这么一看思路就很简单了
倒数第一位 = 1 + 2 + 3 + 4 + 5 + 6
倒数第二位 = 1 + 2 + 3 + 4 + 5
倒数第三位 = 1 + 2 + 3 + 4
倒数第四位 = 1 + 2 + 3
倒数第五位 = 1 + 2
倒数第六位 = 1
由于数据很大 所以需要用string存下来再进行计算

c++代码

#include<iostream>
#include<algorithm>
#include<cstring>
#include<vector>

using namespace std;
string x;
int n;

void print(vector<int> &res)
{
    for(auto t:res) cout<<t;
    cout<<endl;
}

int main()
{
    cin>>x;
    n=x.size();
    int sum=0;
    for(int i=0;i<n;i++)
    {
        sum+=x[i]-'0';
    }
    vector<int> res;
    int t=0;
    for(int i=n-1;i>=0;i--)
    {
        t+=sum;
        res.push_back(t%10);
        t/=10;
        sum-=(x[i]-'0');
    }
    if(t) res.push_back(t);
    reverse(res.begin(),res.end());
    print(res);
}

F - Swap and Sort

题意

给你一个全排列 P i P_i Pi,再给 M M M种操作,第 i i i种操作可以交换P中的 a i a_i ai b i b_i bi位置上的数字
问能否在 5 ∗ 1 0 5 5*10^5 5105个操作之内让P变为有序
如果可以输出顺序,不可以输出-1

思路

这一题其实是AcWing4084号码牌的简化版
先来讲讲4084
其实这一题的算法很简单只不过思路难度中等偏上
我们将这个问题想象成一个图,将可达的两个点中间连一条边
那么最终的问题就转化成了能否通过交换操作使得每个点的a[i]变为i
那就很自然的就想到了并查集(联通块+可达问题),只要在一个联通块里面,那么a[i]就是可以通过交换操作变为i的。

c++代码

#include<iostream>
#include<algorithm>
#include<cstring>

using namespace std;

const int N=110;

int f[N],a[N],d[N];
int n;

int find(int x)
{
    if(x!=f[x])
        f[x]=find(f[x]);
    return f[x];
}

void merge(int a,int b)
{
    a=find(a);
    b=find(b);
    f[a]=b;
}

int main()
{
    cin>>n;
    for(int i=1;i<=n;i++) scanf("%d",&a[i]);
    for(int i=1;i<=n;i++) scanf("%d",&d[i]);
    for(int i=1;i<=n;i++) f[i]=i;
    for(int i=1;i<=n;i++)
    {
        if(i-d[i]>=1) merge(i-d[i],i);
        if(i+d[i]<=n) merge(i+d[i],i);
    }
    for(int i=1;i<=n;i++)
    {
        int aa=find(i);
        int bb=find(a[i]);
        if(aa!=bb)
        {
            puts("NO");
            return 0;
        }
    }
    puts("YES");
}

这一题与号码牌这一题不同的就是,我们的目的就是不仅要判断能否可以通过交换使a[i]=i同时要记录交换的路径
我们的思路就是:
1.找叶子结点(用一个结点来处理整个联通块)
2.对于每个叶子结点需要讲a[i]变为i需要从哪个点转换过来(dfs 就是通过加边要a[i]与i直接可达)
dfs(u,v,fa):u是目前所在的节点 目的:s[u]=v fa是父节点为了防止递归回去
当s[u]==v说明我们找到了这个节点return true
此时我们需要将s[u]与s[j]交换 并且将这条边加入ans

c++代码

#include<iostream>
#include<cstring>
#include<algorithm>
#include<unordered_map>
#include<vector>
#include<queue>

using namespace std;

const int N=5e5+10,M=2*N;

int f[N],s[N];
int e[M],ne[M],h[N],id[N],idx;
bool st[N];
vector<int> ans;

void add(int a,int b,int c)
{
    e[idx]=b;
    ne[idx]=h[a];
    id[idx]=c;
    h[a]=idx++;
}

int find(int x)
{
    if(x!=f[x])
        f[x]=find(f[x]);
    return f[x];
}

bool merge(int a,int b)
{
    a=find(a);
    b=find(b);
    if(a!=b) 
    {
        f[a]=b;
        return true;
    }
    return false;
}

bool dfs(int u,int v,int fa)//将s[u]改为v
{
    if(s[u]==v) return true;
    for(int i=h[u];~i;i=ne[i])
    {
        int j=e[i];
        if(j==fa) continue;
        if(dfs(j,v,u))
        {
            swap(s[u],s[j]);
            ans.push_back(id[i]);
            return true;
        }
    }
    return false;
}

void leaf(int u)
{
    st[u]=true;
    for(int i=h[u];~i;i=ne[i])
    {
        int j=e[i];
        if(st[j])continue;
        leaf(j);
    }
    dfs(u,u,-1);
}

int main()
{
    memset(h,-1,sizeof h);
    int n;
    cin>>n;
    for(int i=1;i<=n;i++) f[i]=i;
    for(int i=1;i<=n;i++) scanf("%d",&s[i]);
    int m;
    cin>>m;
    for(int i=1;i<=m;i++)
    {
        int a,b;
        scanf("%d %d",&a,&b);
        if(merge(a,b))
        {
            add(a,b,i);
            add(b,a,i);
        }
    }
    //当(st[i],i)在一个联通块里面就可以交换--->构造生成树
    for(int i=1;i<=n;i++)
    {
        int a=find(s[i]);
        int b=find(i);
        if(a!=b)
        {
            puts("-1");
            return 0;
        }
    }
    //1.找到叶子结点
    for(int i=1;i<=n;i++)
    {
        if(!st[i]) 
        {
            leaf(i);
            // cout<<"i = "<<i<<endl;
        }
    }
    cout<<ans.size()<<endl;
    for(auto t:ans) cout<<t<<" ";
    cout<<endl;
}

G - Strongest Takahashi

题意

现在有一个NxN的方格,由‘#’与‘.’组成,现在需要消灭里面所有的‘#’,每次可以每次选择一个正整数D每次消灭D*D的矩阵中的‘#’,花费为D,求消灭所有‘#’的最小花费

思路

算法:dp
dp[x1][y1][x2][y2]:消灭(x1,y1),(x2,y2)内所有的’#'所需要的最小操作次数
每次状态转移的方式有2种,一种横着切一刀一种竖着切一刀
65049A1BF9F0543237615C3DADF2ED46.png
因为我比较懒所以就用的记忆画搜索嘿嘿

c++代码

#include<iostream>
#include<algorithm>
#include<cstring>

using namespace std;

const int N=60;
int n;
char g[N][N];
int f[N][N][N][N];

int dp(int r1,int c1,int r2,int c2)
{
    if(f[r1][c1][r2][c2]!=-1) return f[r1][c1][r2][c2];
    if(r1==r2&&c1==c2) return g[r1][c1]=='#';
    f[r1][c1][r2][c2]=max(r2-r1+1,c2-c1+1);
    for(int i=r1;i<r2;i++) f[r1][c1][r2][c2]=min(f[r1][c1][r2][c2],
    dp(r1,c1,i,c2)+dp(i+1,c1,r2,c2));
    for(int i=c1;i<c2;i++) f[r1][c1][r2][c2]=min(f[r1][c1][r2][c2],
    dp(r1,c1,r2,i)+dp(r1,i+1,r2,c2));
    return f[r1][c1][r2][c2];
}

int main()
{
    memset(f,-1,sizeof f);
    cin>>n;
    for(int i=1;i<=n;i++) scanf("%s",g[i]+1);
    cout<<dp(1,1,n,n)<<endl;
}

Ex - Manhattan Christmas Tree

题意

在二维坐标系下有N个圣诞树,每个圣诞树的坐标是( x i x_i xi, y i y_i yi)
接下来有Q个询问
对于每个询问给出3个整数a,b,k 你要找出距离(这里的距离指的是曼哈顿距离)在(a,b)之间距离这个点第k近的圣诞树的距离

算法

(二分+树状数组)

首先引入知识点 曼哈顿距离与切比雪夫距离的转化
因为对于曼哈顿距离我们并不是很好求第k小的距离,所以在这里引入切比雪夫距离,这样就讲第k小的距离转换成为计算矩形中点的个数
首先将坐标点( x i x_i xi, y i y_i yi)转化成( x i x_i xi + y i y_i yi, x i x_i xi - y i y_i yi)
因为树状数字中不出现0 所以在这里我们将( x i x_i xi, y i y_i yi)转化成( x i x_i xi + y i y_i yi + 1, x i x_i xi - y i y_i yi + 1)
怎样计算矩形中点的个数呢?
在这里采用树状数组来存储每个转化后的x坐标对应的每个y
那样每次计算dis在(a,b)的个数就是寻找 大于b的所有点的数量-大于等于a的所有点的数量
我们知道怎么求矩形中点的个数就可以开始二分了,在这里我们二分的是距离
每次check(a,b,k,mid)
首先要把(a,b)转换为切比雪夫坐标再进行计算(xx-mid,xx+mid,yy-mid,yy+mid)这个矩形中点的个数
需要注意的是 因为在这里我们需要所有的tr[i]中的点是有序的,所以我们在这里需要将切比雪夫坐标系按照y排序在进行加入树状数组中

c++代码
#include<iostream>
#include<cstring>
#include<algorithm>
#include<vector>

using namespace std;

typedef pair<int,int> pii;
#define x first
#define y second
const int N=2e5+10;
pii a[N];
vector<int> tr[N];
int n;

int lowbit(int x)
{
    return x&(-x);
}

void add(int x,int y)
{
    for(int i=x;i<N;i+=lowbit(i))
        tr[i].push_back(y);
}

int sum(int x,int a,int b)
{
    int res=0;
    int i=x;
    for(;i;i-=lowbit(i))
    {
        res+=upper_bound(tr[i].begin(),tr[i].end(),b)-lower_bound(tr[i].begin(),tr[i].end(),a);
    }
    return res;
}

int query(int x,int y,int a,int b)
{
    x=max(x,1);
    y=max(y,0);
    x=min(x,N-1);
    y=min(y,N-1);
    return sum(y,a,b)-sum(x-1,a,b);
}

bool check(int x,int y,int k,int mid)
{
    int xx=x+y+1;
    int yy=x-y+1;
    return query(xx-mid,xx+mid,yy-mid,yy+mid)>=k;
}

static bool cmp(pii a,pii b)
{
    return a.y<b.y;
}

int main()
{
    int n;
    cin>>n;
    for(int i=1;i<=n;i++)
    {
        int x,y;
        scanf("%d %d",&x,&y);
        a[i]=make_pair(x+y+1,x-y+1);
    }
    sort(a+1,a+n+1,cmp);
    for(int i=1;i<=n;i++)
    {
        add(a[i].x,a[i].y);
    }
    int q;
    cin>>q;
    while(q--)
    {
        int a,b,k;
        scanf("%d %d %d",&a,&b,&k);
        int l=0,r=2e5+10;
        while(l<r)
        {
            int mid=(l+r)>>1;
            if(check(a,b,k,mid)) r=mid;
            else l=mid+1;
        }
        printf("%d\n",r);   
    }
}
  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
AtCoder Beginner Contest 134 是一场 AtCoder 的入门级比赛,以下是每道的简要题解: A - Dodecagon 目描述:已知一个正十二边形的边长,求它的面积。 解思路:正十二边形的内角为 $150^\circ$,因此可以将正十二边形拆分为 12 个等腰三角形,通过三角形面积公式计算面积即可。 B - Golden Apple 目描述:有 $N$ 个苹果和 $D$ 个盘子,每个盘子最多可以装下 $2D+1$ 个苹果,求最少需要多少个盘子才能装下所有的苹果。 解思路:每个盘子最多可以装下 $2D+1$ 个苹果,因此可以将苹果平均分配到每个盘子中,可以得到最少需要 $\lceil \frac{N}{2D+1} \rceil$ 个盘子。 C - Exception Handling 目描述:给定一个长度为 $N$ 的整数序列 $a$,求除了第 $i$ 个数以外的最大值。 解思路:可以使用两个变量 $m_1$ 和 $m_2$ 分别记录最大值和次大值。遍历整个序列,当当前数不是第 $i$ 个数时,更新最大值和次大值。因此,最后的结果应该是 $m_1$ 或 $m_2$ 中较小的一个。 D - Preparing Boxes 目描述:有 $N$ 个盒子和 $M$ 个物品,第 $i$ 个盒子可以放入 $a_i$ 个物品,每个物品只能放在一个盒子中。现在需要将所有的物品放入盒子中,每次操作可以将一个盒子内的物品全部取出并分配到其他盒子中,求最少需要多少次操作才能完成任务。 解思路:首先可以计算出所有盒子中物品的总数 $S$,然后判断是否存在一个盒子的物品数量大于 $\lceil \frac{S}{2} \rceil$,如果存在,则无法完成任务。否则,可以用贪心的思想,每次从物品数量最多的盒子中取出一个物品,放入物品数量最少的盒子中。因为每次操作都会使得物品数量最多的盒子的物品数量减少,而物品数量最少的盒子的物品数量不变或增加,因此这种贪心策略可以保证最少需要的操作次数最小。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值