目录
涵盖知识点:思维、贪心、数学。
比赛链接:传送门
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<<"
";
}
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<<"
";
}
return 0;
}
D - Minimum Euler Cycle
题意: 给定(n)个顶点的完全有向图。要求求出字典序最小的欧拉回路的某个区间。
题解: 观察题目中给的(n=3)的样例可以看出(left[ 1,2,1,3
ight] left[ 2,3
ight] left[ 1
ight])
就是说对(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<<"
";
}
}
E - Divisor Paths
题意:
给定一个正整数(D), 以此建图
- 每个节点都是(D)的因子
- (x,y(x>y))节点之间有无向边存在的条件是(y)是(x)因子且(frac{x}{y})是质数
- (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{u}{v}),则(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
", calc(u / w) * calc(v / w) % mod);
}
return 0;
}