您的位置:首页 > 汽车 > 新车 > atcoder350,351,352,353,354,355期部分题解

atcoder350,351,352,353,354,355期部分题解

2024/12/31 5:01:18 来源:https://blog.csdn.net/m0_69478376/article/details/139370305  浏览:    关键词:atcoder350,351,352,353,354,355期部分题解

声明:有些题感觉已经说到很明白了,就先不写代码了,有空会补上

目录

350D: new friend

350E: toward 0

351D:Grid and Magnet

352D:permutation  subsequence

353C: sigma problem

353D: another sigma problem

354C: atcoder magics

355C: bingo2

355D: intersecting intervals 

附加题:火烧赤壁


希望读者可以有所收获 

        

        

350D: new friend

就是问你,最终有多少对这种关系:x和y是朋友,y和z是朋友,但是x和z不是朋友。你也把这种关系理解成y是中介方,但是我们要明白一点,x和y和z一定是在同一个联通块中。所以选点一定就是在联通块中选择。

也就是说:在同一个联通块中,如果x和y是朋友关系,那么它们不满足题意;如果x和y不是朋友关系,那么它们就满足题意。所以我们只需要找所有联通块中有多少对新朋友,加起来,然后减去原来的朋友数即可。

(为什么我会想到联通块呢?因为在我们做题的时候,尤其是图论,一定要尽量把题给抽象一下,你才能想到考察的知识点)

最后,要求联通块中的新朋友对数,就需要求联通块中的点数,我们可以dfs跑联通块,然后返回这个联通块中的点数,同时给遍历过的点打上标签,然后去跑别的联通块;我们也可以直接并查集来做,维护祖宗节点的cnt值,存放该集合的点数。

        

        

350E: toward 0

题意:给一个N,然后每轮有两种操作,一个是花费x变成[N/A],另一个是花费y变成[N/b],b等概率取1,2,3,4,5,6。 

分析:一道期望dp,感觉和概率dp差不多,大致思路都是dp[起始状态]=初始概率或者初始期望,然后找每轮之间的关系进行转移,一直dp到最终状态就行了。

我们先找每轮之间的关系:

我们设置f[N]表示N变成0的期望花费,初始f[0]=0

f[N]=Y+(f[N/6]+f[N/5]+f[N/4]+f[N/3]+f[N/2]+f[N/1])/6

修改一下变成:f[N]=6/5*Y+(f[N/6]+f[N/5]+f[N/4]+f[N/3]+f[N/2])/5

所以:f[N]=min(X+f[N/A],6/5*Y+(f[N/6]+f[N/5]+f[N/4]+f[N/3]+f[N/2])/5)

接下来就是转移就完事了,明显借助dfs来完成转移更加方便。

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
unordered_map<ll,double>mp;
ll N,A,X,Y;
double f(ll x){if(x==0)return 0; //初始值if(mp.count(x))return mp[x]; //记忆化double ret2=0,ret1=f(x/A)+X;for(int i=2;i<=6;i++)ret2+=f(x/i);ret2=(ret2+6*Y)/5;return mp[x]=min(ret1,ret2);
}
int main(){cin>>N>>A>>X>>Y;printf("%.9f",f(N));
}

代码为什么要用double呢?因为存在性质:[[N/a]/b]=[N/ab],所以要保持精度。 

        

        

351D:Grid and Magnet

 题意:HxW的网格,' . '代表可以走,' # '代表有磁铁,会吸住上下左右的格子,导致不能再移动,要求输出从某一个点能走的最多格子。

考的还是联通块,我们可以把每个磁铁周围会被吸住的格子打上标记,然后我们去搜图中的每一个联通块,并统计每个联通块的块数,最后输出最大的块数即可。

        

        

352D:permutation  subsequence

题意:有一个序列1~n的某一排序,我们要在此排序中找到长度最小的包含长度为k的任意自然数段。

我这里强调一下这种自然数某一排序题的常见思路:就是分析pos数组,pos数组就是每个数的位置信息。

比如:10 1 6 8 7 2 5 9 3 4   构造pos数组如下:

数组:2 6 9 10 7 3 5 4 8 1(表示每个1~n的位置信息)

我们开始考虑1~5的最小符合题意的长度:处理2 6 9 10 7即可,max-min=8,说明长度为8

考虑2~6:max-min=7

考虑3~7:max-min=7

考虑4~8:max-min=5

考虑5~9:max-min=5

考虑6~10:max-min=7

所以答案就是5

也就是我们只需要求出每个区间的极差即可,使用滑动窗口就可以了。

        

        

353C: sigma problem

首先要理解这个玩意:

它意思就是

    for(int i=1;i<=n-1;i++)for(int j=i+1;j<=n;j++){ans+=f(a[i],a[j]);}

但是我们肯定不能直接这样做,因为太慢了,那么我们就要找规律了:

那么最直接的想法就是“变加为乘”,以此来加速。

我们不难发现:全部的相加过程中:

a[1]+a[2]   a[1]+a[3]   a[1]+a[4]……a[1]+a[n]

a[2]+a[3]   a[2]+a[4]……a[2]+a[n]

……

也就是说如果我们先不考虑取模的情况时,每个数a[i]都会被加n-1次

然后我们再考虑取模的情况:

这里如果没有发现a[i]<10^8这个细节的话,估计就很难做出来了,如何使用这个条件呢?

我们会发现a[i]+a[j]在mod10^8的过程中最多也就是减去了一次10^8,所以我们在把所有的a[i]+a[j]大于10^8的个数找出来,然后减去10^8就行了。

#include <bits/stdc++.h>
using namespace std;
int a[300000];
int mod=100000000;
int main(){int n;cin>>n;long long ans=0;for(int i=0;i<n;i++){cin>>a[i];ans+=a[i]*(n-1ll);}sort(a,a+n);for(int i=0;i<n;i++){ans-=mod*1ll*(a+n-lower_bound(a+1+i,a+n,mod-a[i]));//另外一个数一定比当前的数更大,所以从a+1+i开始找}cout<<ans<<'\n';
}

        

        

353D: another sigma problem

这个还是要“变加为乘”,但是要注意的是,这个题不可以进行排序,因为f(x,y)不等于f(y,x),所以我们要顺序来。对于f(a[i],a[j]),a[j]本身就会加一次,a[i]乘一下a[j]的倍率。

首先我们可以发现,每个数都会加上n-1遍,所以这些贡献可以单独求出来。

剩下的倍率问题:对于a[i],我们可以求它后面数每个数的位数,然后求出每个倍率然后加起来

 

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
ll mod=998244353;
ll suf[200005],a[200005];
ll get(ll x){ll ans=1;while(x)x/=10,ans*=10;return ans;
}
int main(){int n;cin>>n;for(int i=1;i<=n;i++)cin>>a[i];ll ans=0;for(int i=n;i>=1;i--){suf[i]=(suf[i+1]+get(a[i]))%mod; //每个数要乘的倍率的和(后缀和)}for(int i=1;i<=n;i++){ans+=a[i]*(i-1)%mod;//前半部分的贡献ans%=mod;ans+=suf[i+1]*a[i]%mod;//后半部分的贡献ans%=mod;}cout<<ans<<'\n';
}

        

        

354C: atcoder magics

题意:有N张卡片,每张卡片上有A和C两个数,分别表示力量和花费,我们可以进行操作:

选择两个卡片,如果A1>A2,C1<C2,我们就丢掉2卡片。问我们最终可以剩下哪些卡片?

做这种题的一个直觉就是排序:如果我们按照A从大到小排好序后,就不用再考虑A了,只需要考虑C。如果当前的卡片C值比前面的卡片的C值都小,那就留下他,否则就丢掉这个卡片。

#include <bits/stdc++.h>
using namespace std;
struct card{int val,cost,id;
}a[200005];
bool cmp(card x,card y){return x.val>y.val;
}
int main(){int n;cin>>n;for(int i=1;i<=n;i++){int val,cost;cin>>val>>cost;a[i]={val,cost,i};}sort(a+1,a+1+n,cmp);int minn=2e9;vector<int> ans;for(int i=1;i<=n;i++){if(a[i].cost<minn){ans.push_back(a[i].id);minn=a[i].cost;}}sort(ans.begin(),ans.end());cout<<ans.size()<<'\n';for(auto i:ans){cout<<i<<' ';}return 0;
}

        

        

355C: bingo2

题意:有一个N*N的网格,依次填入1,2,3,4……n^2(如图),然后一共有T轮,每轮都会填一个数,问至少几轮后才有一列或者一行,或者一个对角线被填充?

考察的就是如何用vis表示一个对角线,不会的回去看看CSDN中的八皇后问题,填入一个就vis++,然后检查对应的行,列,对角线,看看什么时候可以满足题意。

        

        

355D: intersecting intervals 

题意:有n个区间,问你一共有多少对区间相交?

先拆开分析:

1开始 7开始 3开始

5结束 8结束 7结束

我们排序:

1开始

3开始

5结束

7开始

7结束

8结束

我们要统计每个区间开始出现的时候,已经有多少个区间存在了,那么此时就会增加多少个相交区间,也就是我们要关心目前有多少个区间往后面走,当开始出现一个区间时候cnt++,当开始减少一个区间时候cnt--

排序的时候注意相同时间开始在前,结束在后(因为在某一点都同时有开始和结束也算相交)

1开始  cnt=1

3开始  cnt=2  ans=1

5结束  cnt=1  

7开始  cnt=2  ans=2

7结束  cnt=1

8结束  cnt=0

再来一个样例

1开始  cnt=1

2开始  cnt=2  ans=1

3开始  cnt=3  ans=3

4结束  cnt=2

5结束  cnt=1

6结束  cnt=0

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
struct node{int v,op; //op=1为结束,op=0为开始
};
vector<node>ve;
bool cmp(node x,node y){if(x.v==y.v)return x.op<y.op;//先返回起点再返回终点return x.v<y.v;
}
int main(){int n;cin>>n;for(int i=1;i<=n;i++){int l,r;cin>>l>>r;ve.push_back({l,0});ve.push_back({r,1});}sort(ve.begin(),ve.end(),cmp);int cnt=0;ll ans=0;for(int i=0;i<ve.size();i++){int v=ve[i].v,op=ve[i].op;if(op==0)ans+=cnt,cnt++;else cnt--;}cout<<ans<<'\n';return 0;
}

看完这题,建议再看一道很像的题。

        

        

附加题:火烧赤壁

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=2e4+10;
ll ans;
int n;
struct node{ll x;ll y;}e[N];
bool cmp(node a,node b){if(a.x==b.x)return a.y<b.y;return a.x<b.x; 
}
int main(){cin>>n;ll a,b;for(int i=1;i<=n;i++){scanf("%lld%lld",&a,&b);e[i].x=a;e[i].y=b-1;}sort(e+1,e+1+n,cmp);ll end=e[1].y;ans+=e[1].y-e[1].x+1;for(int i=2;i<=n;i++){if(end>=e[i].x){if(end>=e[i].y)continue;ans+=e[i].y-end;end=e[i].y;}else {ans+=e[i].y-e[i].x+1;end=e[i].y;	}}cout<<ans;
}

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com