Educational Codeforces Round 85 (Rated for Div. 2)【ABCDE】(题解)
[toc]
涵盖知识点:思维、贪心、数学。
比赛链接:传送门
A - Level Statistics
题意: 按照时间顺序给出游戏进行次数和通关次数,判断是否合理。
题解: 保证相邻区间内和总区间进行次数大于等于通关次数且非递减即可。
Accept Code:
#include <bits/stdc++.h>
using namespace std;
int main(){
int t;
cin>>t;
while(t--){
int n;
cin>>n;
int maxp=0,maxc=0;
bool flag=true;
while(n--){
int p,c;
cin>>p>>c;
if(p<maxp||c<maxc||p<c||p-maxp<c-maxc){
flag=false;
}
maxp=max(p,maxp),maxc=max(c,maxc);
}
puts(flag?"YES":"NO");
}
return 0;
}
B - Middle Class
题意: 规定财富大于等于某个阈值即为有钱人,每次操作可以选取一堆人,让他们每个人的财富变为他们的平均值。
题解: 从大到小排序后贪心。
Accept Code:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=1e5+10;
ll a[maxn];
int main(){
int t;
cin>>t;
while(t--){
int n;
ll x;
cin>>n>>x;
for(int i=1;i<=n;i++)cin>>a[i];
sort(a+1,a+n+1);
reverse(a+1,a+1+n);
int cnt=0;
ll sum=0;
for(int i=1;i<=n;i++){
sum+=a[i];
if(sum>=x*i)cnt++;
else break;
}
cout<<cnt<<"\n";
}
return 0;
}
C - Circle of Monsters
题意: 有一圈怪兽,每个怪兽有$a_i$点生命值,每射击1次怪兽会掉1点生命值,当怪兽死亡时会对下一个怪兽造成$b_i$点伤害,要想消灭所有怪兽最少需要射击多少次。
题解: 先把所有血量修到前一个怪死亡后会造成的伤害,然后选一个最少的把怪打死的伤害即可。
Accept Code: 学到了rotate的用法。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=3e6+10;
const ll inf=0x3f3f3f3f3f3f3f3f;
ll a[maxn],b[maxn];
int main(){
int t;
cin>>t;
while(t--){
ll n,ans=inf,cnt=0;
scanf("%lld",&n);
for(int i=0;i<n;i++)scanf("%lld%lld",&a[i],&b[i]);
rotate(b,b+n-1,b+n);
for(int i=0;i<n;i++){
if(a[i]>b[i])cnt+=a[i]-b[i];
}
for(int i=0;i<n;i++){
ll res=0;
if(a[i]>b[i])res-=(a[i]-b[i]);
res+=a[i];
ans=min(ans,res);
}
cout<<cnt+ans<<"\n";
}
return 0;
}
D - Minimum Euler Cycle
题意: 给定$n$个顶点的完全有向图。要求求出字典序最小的欧拉回路的某个区间。
题解: 观察题目中给的$n=3$的样例可以看出$\left[ 1,2,1,3 \right] \left[ 2,3 \right] \left[ 1 \right]$
就是说对$1 \sim n$的每一个节点,每次从当前点出发依次走到下一个节点再回来是字典序最小的,但是当前点走到$n$时不能回来,要直接走到下一个节点去,不然就会无路可走。最终才回到1节点
再观察可以发现对于$1 \sim n$节点,每组有$2(n-i)$个节点,同时奇数位的节点就是它本身,偶数位为当前组内位置除$2+i$。
Accept Code:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
int main(){
int t;
cin>>t;
while(t--){
ll n, l, r;
cin>>n>>l>>r;
ll limit = 1ll * n * (n - 1) + 1;
ll sum = 0, id = 1;
for (ll i = l; i <= r && i < limit; i++){
while (sum + (n - id) * 2 < i)
sum += (n - id) * 2, id++;
ll d = i - sum;
printf("%lld ", (d & 1) ? id : d / 2 + id);
}
if (r == limit)
cout<<"1 ";
cout<<"\n";
}
}
E - Divisor Paths
题意:
给定一个正整数$D$, 以此建图
- 每个节点都是$D$的因子
- $x,y(x>y)$节点之间有无向边存在的条件是$y$是$x$因子且$\frac$是质数
- $x,y$之间若有边,则边权是$x$的因子中不是$y$的因子的个数
给出$q$组询问,求$u,v$之间的最短路径条数
题解: 首先可以想到,一个节点$u$向另一个节点$v$转移的过程中总是通过抛出一些因子来进行的,其中$v|u$
那么uu到vv的最短路径就是$d(u)-d(x_1)+d(x_1)-d(x-2)+\ldots+d(x_y)-d(v)=d(u)-d(v)$,其中$d(x)$为$x$的因子个数,因此当一个节点能被令一个节点整除时,两节点间的最短路径就是两节点的因子个数之差。考虑到$u$和$v$两者不为倍数关系时,因为节点的转移需要通过抛出一些因子来实现,所以需要找个中间节点$x$使得$u->x$并且$v->x$,那么显而易见$x$肯定为$u$和$v$的公因子,要使的路径最短就是使得$d(u)-d(x)+d(v)-d(x)$最小,那么就是使得$d(x)$最大,所以$x=gcd(u,v)$,那么最终所求就是$g(u,gcd(u,v))*g(v,gcd(u,v))$,其中$g(u,v)$为$u$和$v$两点间最短路径的条数
因为一个节点$u$向另一个节点$v$转移的过程中总是通过抛出一些因子来进行的,那么条数就是这些中间因子的抛出顺序,那么对于$g(u,v)$,其中$v|u$,令$x=\frac$,则$g(u,v)=\frac{t!}{p_1!*p_2!...p_n!}$,其中$t$为$x$中各质因子的幂之和,$p_i$为$x$中某个质因子的幂,就是排列组合里重复剔除问题除于它的阶乘
Accept Code:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll mod = 998244353;
map<ll, ll> m;
ll calc(ll x){
if (m[x])
return m[x];
ll res = 0, y = x;
for (ll i = 2; i * i <= y; i++)
if (y % i == 0){
while (y % i == 0)
y /= i;
res = (res + calc(x / i)) % mod;
}
if (y > 1)
res = (res + calc(x / y)) % mod;
return m[x] = res;
}
int main(){
ll d, q;
cin>>d>>q;
m[1] = 1;
while (q--){
ll u, v;
cin>>u>>v;
ll w = __gcd(u, v);
printf("%lld\n", calc(u / w) * calc(v / w) % mod);
}
return 0;
}