AtCoder Beginner Contest 160题解
第一次打atcoder,感觉beginner场和cfdiv3难度差不多,就是A-E难度和F差距有点大,而且居然#ifndef ONLINE_JUDGE读写入文件会RE,白给了2发罚时
A.
题意:给一个长度为6的字符串当第3位和第4位相同并且第5位和第6位相同时输出Yes否则输出No
题解:字符串模拟题
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include <bits/stdc++.h> using namespace std; typedef long long ll; const int maxn = 1e5+5; const int mod = 1e9+7; ll qpow(ll a,ll b){ll res=1;for(;b;b>>=1){if(b&1)res=res*a%mod;a=a*a%mod;}return res;} int main() { string a; cin>>a; if(a[2]==a[3]&&a[4]==a[5])printf("Yes\n"); else printf("No\n"); return 0; }
B.
题意:有6种硬币面值分别为500,100,50,10,5,1,一个500面值的硬币会获得1000的愉悦感,一个5面值的硬币会获得5的愉悦感,现在问你有X日币,把他们换成硬币后能获得的最多的愉悦感是多少。
题解:贪心,先选500的再选5的。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include <bits/stdc++.h> using namespace std; typedef long long ll; const int maxn = 1e5+5; const int mod = 1e9+7; ll qpow(ll a,ll b){ll res=1;for(;b;b>>=1){if(b&1)res=res*a%mod;a=a*a%mod;}return res;} int main() { ll x; scanf("%lld",&x); ll ans = x/500*1000; x-=x/500*500; ans+=x/5*5; cout<<ans<<endl; return 0; }
C.
题意:给出一个周长为K的圆上的n个点,求任选择一个点为起点,遍历完这n个点所需走的最短路径。
题解:枚举每个起点,因为要遍历完n个点所以最后停下的位置肯定是与它相邻的两个点中的一个,可以O(1)更新答案
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include <bits/stdc++.h> using namespace std; typedef long long ll; const int maxn = 2e5+5; const int mod = 1e9+7; ll qpow(ll a,ll b){ll res=1;for(;b;b>>=1){if(b&1)res=res*a%mod;a=a*a%mod;}return res;} int a[maxn]; int main() { int n,k; scanf("%d%d",&k,&n); for(int i = 1;i <= n;++i)scanf("%d",&a[i]); int ans = 0; ans=a[n]-a[1]; for(int i = 2;i <= n;++i) { int tmp = a[i-1]+k-a[i]; ans=min(ans,tmp); } cout<<ans<<endl; return 0; }
D.
题意:有一个n个顶点n条边的无向图,其中n-1条边是i和i-1,还有一条指定的边x-y,对于1<=k<n,求图上最短距离为k的无序点对数
题解:由于n<=2000,所以可以枚举每个起点i,bfs求i到每个点的最短路dist[j],然后ans[dist[j]]++,因为是无序点对所以答案需要除以2。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include <bits/stdc++.h> using namespace std; typedef long long ll; const int maxn = 1e5+5; const int mod = 1e9+7; ll qpow(ll a,ll b){ll res=1;for(;b;b>>=1){if(b&1)res=res*a%mod;a=a*a%mod;}return res;} int ans[maxn]; int n,x,y,mi; int dist[maxn]; void bfs() { memset(dist,-1,sizeof(dist)); dist[mi]=0; queue<int>q; q.push(mi); while(!q.empty()) { int now = q.front(); q.pop(); if(now+1<=n&&dist[now+1]==-1){ dist[now+1]=dist[now]+1; q.push(now+1); } if(now-1<=n&&dist[now-1]==-1){ dist[now-1]=dist[now]+1; q.push(now-1); } if(x==now) { if(y>=mi&&dist[y]==-1){ dist[y]=dist[now]+1; q.push(y); } } if(y==now) { if(y>=mi&&dist[x]==-1){ dist[x]=dist[now]+1; q.push(x); } } } } int main() { scanf("%d%d%d",&n,&x,&y); for(int i = 1;i < n;++i) { mi = i; bfs(); for(int j = i+1;j <= n;++j) ans[dist[j]]++; } for(int i = 1;i < n;++i)printf("%d\n",ans[i]); return 0; }
E.
题意:有A个红色苹果,B个绿色苹果,C个无色苹果,你可以把无色苹果染成红色或蓝色的,你需要吃掉X个红苹果,Y个绿苹果,每个苹果有一个美味值,求能够获得的最大美味值。
题解:贪心,先把所以苹果按美味值从大到小排序,选出前x+y个苹果,统计里面红色苹果数量和绿色苹果数量,如果出现了红色数量大于X或绿色数量大于Y的情况,那么就减掉前x+y个里面美味值小的红色或蓝色苹果,再在后a+b+c-x-y个苹果里选出对应数量的红/蓝苹果。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include <bits/stdc++.h> using namespace std; typedef long long ll; const int maxn = 1e5+5; const int mod = 1e9+7; ll qpow(ll a,ll b){ll res=1;for(;b;b>>=1){if(b&1)res=res*a%mod;a=a*a%mod;}return res;} struct node { int val,type; friend bool operator < (node a,node b){ return a.val>b.val; } }a[maxn*3]; int main() { int x,y,r,g,b; cin>>x>>y>>r>>g>>b; int tot = 0; for(int i = 1;i <= r;++i) { tot++; a[tot].type=1; scanf("%d",&a[tot].val); } for(int i = 1;i <= g;++i) { tot++; a[tot].type=2; scanf("%d",&a[tot].val); } for(int i = 1;i <= b;++i) { tot++; a[tot].type=3; scanf("%d",&a[tot].val); } sort(a+1,a+1+tot); int cnt1,cnt2; cnt1=cnt2=0; ll sum = 0; for(int i = 1;i <= x+y;++i) { if(a[i].type==1)cnt1++; else if(a[i].type==2)cnt2++; if(a[i].type==1&&cnt1>x)continue; if(a[i].type==2&&cnt2>y)continue; sum+=a[i].val; } if(cnt1>x) { int res = cnt1-x; for(int i = x+y+1;i <= x+y+r&&res;++i) { if(a[i].type!=1)sum+=a[i].val,res--; } } if(cnt2>y) { int res = cnt2-y; for(int i = x+y+1;i <= x+y+r&&res;++i) { if(a[i].type!=2)sum+=a[i].val,res--; } } cout<<sum<<endl; return 0; }
F.
题意:给定一个n个顶点的无根树,对于1<=k<=n,求以节点k为起点给所有节点打上标号的方案数。打标号的规则是首先给节点K写上1,再按照2,3,...,n的顺序随机的选择任意一个与已经打过标号的节点相邻的未打标号的节点写上数字。
题解:先考虑如何求以1为根的方案数。节点1的每颗子树的每一个确定的方案中各个点的出现顺序一定是确定的,以son[1]表示1的子节点,可以推出ans[1]=(n-1)!*Π(ans[son[1]])/Π(size[son[i]])!,根据这个式子再换根dp求出以每个节点为根的答案。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include <bits/stdc++.h> using namespace std; typedef long long ll; const int maxn = 2e5+5; const int mod = 1e9+7; ll qpow(ll a,ll b){ll res=1;for(;b;b>>=1){if(b&1)res=res*a%mod;a=a*a%mod;}return res;} ll d[maxn],p[maxn],size[maxn],ans[maxn],jec[maxn]; int n; vector<int>v[maxn]; void dfs1(int now,int pre) { size[now]=1; d[now]=1; for(int i = 0;i < v[now].size();++i){ int to = v[now][i]; if(to==pre)continue; dfs1(to,now); size[now]+=size[to]; } d[now] = jec[size[now]-1]; for(int i = 0;i < v[now].size();++i){ int to = v[now][i]; if(to==pre)continue; d[now]=d[now]*d[to]%mod*qpow(jec[size[to]],mod-2)%mod; } } void dfs2(int now,int pre) { ll tmp=1; if(now!=1) { tmp=ans[pre]*jec[size[now]]%mod*qpow(jec[n-1],mod-2)%mod*qpow(d[now],mod-2)%mod*jec[n-size[now]-1]%mod; } else { for(int i = 0;i < v[now].size();++i){ int to = v[now][i]; if(to==pre)continue; dfs2(to,now); } return ; } ans[now]=jec[n-1]; ans[now]=ans[now]*tmp%mod*qpow(jec[n-size[now]],mod-2)%mod; for(int i = 0;i < v[now].size();++i){ int to = v[now][i]; if(to==pre)continue; ans[now]=ans[now]*d[to]%mod*qpow(jec[size[to]],mod-2)%mod; } for(int i = 0;i < v[now].size();++i){ int to = v[now][i]; if(to==pre)continue; dfs2(to,now); } } int main() { scanf("%d",&n); jec[0]=1; for(int i = 1;i <= n;++i)jec[i]=jec[i-1]*i%mod; for(int i = 1;i < n;++i) { int a,b; scanf("%d%d",&a,&b); v[a].push_back(b); v[b].push_back(a); } dfs1(1,0); ans[1]=d[1]; dfs2(1,0); for(int i = 1;i <= n;++i)printf("%lld\n",ans[i]); return 0; }