Codeforces Round #727 (Div. 2) 解题报告(A-D)
传送门
//别问我为什么现在才发,刚刚翻存库发现了这坑还留着E题没填,懒得填了所以就改成A到D题发上了。。。
总结
感觉还好,赛场上A了ABCD题,E题想出来了思路但没来得及实现。
ABCD感觉全是思维题,用不到什么高级算法。
A. Contest Start(数学)
洛谷传送门
模拟一下,会发现整个数列分成两个部分,前部分是定值,后部分因为后面没有那么多人所以是一个等差数列。
分别求一下即可。
注意开 \(long\ long\) 防炸。
#include<cstdio>
#include<iostream>
#include<cstring>
#include<iomanip>
#include<cmath>
#include<algorithm>
using namespace std;
int k;
long long n,x,t;
int main(){
cin>>k;
while(k--){
cin>>n>>x>>t;
long long num=t/x;
if(num>=n) cout<<(1+n-1)*(n-1)/2<<endl;
else cout<<(1+num)*num/2+(n-num-1)*num<<endl;
}
return 0;
}
B. Love Song(前缀和)
洛谷传送门
其实就是把字母转化为数字后,对于每一个区间,求出:
\[\sum_{i=1}^{26}num[i]*i
\]
用前缀和对每个数字进行预处理即可。
#include<cstdio>
#include<iostream>
#include<cstring>
#include<iomanip>
#include<cmath>
#include<algorithm>
using namespace std;
const int maxn=100005;
int q,n,d[maxn][30];
string s;
int main(){
cin>>n>>q>>s;
for(int i=1;i<=n;i++){
for(int j=1;j<=26;j++){
d[i][j]=d[i-1][j];
}
d[i][s[i-1]-'a'+1]++;
}
for(int i=1;i<=q;i++){
int l,r,ans=0;
scanf("%d%d",&l,&r);
for(int j=1;j<=26;j++){
ans+=(d[r][j]-d[l-1][j])*j;
}
printf("%d\n",ans);
}
return 0;
}
C. Stable Groups(贪心)
洛谷传送门
这个问题本质就是一个区间合并。
相当于一开始给你 \(n\) 个点,排序后左右距离 \(<=x\) 的点在一个区间内,然后你可以加入 \(k\) 个点,使得一些区间合并,要求求出最后最小的区间数量(即做多能合并的数量)。
假设 \(i\) 区间的右端点到 \(i+1\) 区间的左端点的距离为 \(cha\),则需要加入的点数为 \(ceil(\frac{cha}{x})-1\),因为 \(double\) 的范围不够,所以我们要手写一个 \(ceil\)。
这样很显然的贪心就是每次选择合并需要点数最小的两个区间进行合并。
一开始扫一遍数组把间距扔到一个数组或者优先队列里排个序即可。
#include<cstdio>
#include<iostream>
#include<cstring>
#include<iomanip>
#include<cmath>
#include<algorithm>
#include<queue>
using namespace std;
const int maxn=200005;
int n,ans=1;
long long k,x,a[maxn];
priority_queue<long long,vector<long long>,greater<long long> > q;
int main(){
cin>>n>>k>>x;
for(int i=1;i<=n;i++) scanf("%lld",&a[i]);
sort(a+1,a+n+1);
for(int i=2;i<=n;i++){
if(a[i]-a[i-1]>x){
q.push(a[i]-a[i-1]);
ans++;
}
}
while(!q.empty()){
long long cha=q.top();
q.pop();
long long need=cha/x;
if(need*x==cha) need--;
if(k<need) break;
else{
k-=need;
ans--;
}
}
cout<<ans;
return 0;
}
D. PriceFixed(贪心,模拟)
- 性质一:如果有价格为 \(1\) 的并且仍需要购买的物品,一定先进行购买。
- 性质二:不会有买已经达到购买需求的物品以获取新的价格为 \(1\) 的物品行为。
性质一很显然,性质二证明如下:
设当前某个有需求的物品的还需购买 \(j\) 个,将其降到价格为 \(1\) 所需要再购买物品数为 \(k\),则两种策略花费的价格分别为:
- \(k\le j:k+j\) 和 \(2*k+(j-k)=k+j\),可见两者相等。
- \(k>j:k+j\) 和 \(2*k<k+j\),可见直接购买更优。
有了这两条性质,我们可以把物品按照 \(b\) 从小到大排序,模拟一下即可。
#include<cstdio>
#include<iostream>
#include<cstring>
#include<iomanip>
#include<cmath>
#include<algorithm>
using namespace std;
const int maxn=100005;
int n,k=1;
long long now,need,ans;
struct node{
long long a,b;
}x[maxn];
bool cmp(node a,node b){
return a.b<b.b;
}
int main(){
cin>>n;
for(int i=1;i<=n;i++){
scanf("%lld%lld",&x[i].a,&x[i].b);
need+=x[i].a;
}
sort(x+1,x+n+1,cmp);
while(now<need){
if(k>n) break;
long long res=max((long long)0,min(x[k].b-now,need-now));
now+=res;
ans+=min(need-now,x[k].a);
now+=min(need-now,x[k].a);
k++;
}
cout<<need*2-ans;
return 0;
}