P1机房聚餐
直接枚举可能性应该会炸,我们考虑利用二分,二分OIer的人数,然后尝试能否买下即可!
这些写出check的策略!
int check(int num) { for(int i = 1; i <= n; i ++)
food[i] = hug[i] + gre[i] * (num - 1); //算出每个人吃的
sort(food + 1, food + 1 + n); //优先选吃的少(因为穷)
int cra = 0; //统计一下人数!
for(int i = 1; i <= num; i ++)
cra += food[i];
return (cra <= ttf);
}P2数字游戏v2
本题应该是本次模拟赛中最简单的一题,考虑利用前缀和+桶维护,然后利用组合数,即可
s=a[1]*a[4]+a[2]*a[3]+a[0]*(a[0]-1)/2; 注意这个a[0]的情况即可,因为它是任选两个合并而来,其他的都是两两配对
P3翻转字符
前缀和和后缀和的应用
从前向后遍历的过程中,记录字符A对应的 Pi 前缀和 PA,以及字符 B 对应的 Pi 前缀和 PB 。即 PAi 是所有位置 <=i 的字符 A 的能量之和,同理 PBi 是字符 B 的能量之和。
我们考虑枚举最终前缀翻转的位置 ,当这两个差值达到最大时( PAi-PBi ),我们翻转这个前缀, B 能够获得最多的能量。
同理我们倒序遍历,可以求出获得能量最多的后缀翻转。记录这两个最大值中更大的那个就是答案,时间复杂度 O(n) 。
这里面有一个小技巧就是只需要枚举一个即可,另外一个可以通过相减得出
for(int i = 1; i <= n; i++) {
cin >> num[i];
sum[i] = sum[i - 1] + num[i];
}
cin >> s;
for(int i = 0; i < n; i++) {
sumB[i + 1] = sumB[i];
if(s[i] == 'B')
sumB[i + 1] += num[i + 1];
}
long long res = sumB[n];
for(int i = 1; i <= n; i++) {
res = max(res, sumB[n] + sum[i] - 2 * sumB[i]);
res = max(res, sumB[i] * 2 + sum[n] - sumB[n] - sum[i]);
}P4友好匹配
考虑利用LCA,然后利用树上前缀(和树根的权值),然后利用公式计算一下即可!本题没有卡倍增,如果加强一下数据,可以考虑卡O(n)LCA的算法
for(int i=0;i<3;i++){
int z=w[x][i]+w[y][i]-w[lf][i]-w[father[lf]][i];
ans+=z*(z-1)/2;
}P5保留火种
其实就是修建道路
一开始只能想到枚举g去跑最小生成树
是m^2的算法(50pts)
但是其实每次加入的边只有一条
而且之前都不在最小生成树上的边以后也肯定不会在
所以可以建一个新的边的集合存当前生成树的边
这个集合的边最多只有n条
这样复杂度就降为n*m
可以通过本题
#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
long long n, m, top, st[1010100];
long long wG, wS, ans=1e18;
struct node
{
long long x, y, g, s;
}a[1010100];
long long fa[1010100];
long long find(long long x){
if(fa[x]==x)
return x;
return fa[x]=find(fa[x]);
}
bool cmp(node x, node y){
return x.g<y.g;
}
int main()
{
scanf("%lld%lld%lld%lld", &n, &m, &wG, &wS);
for(long long i=1; i<=m; i++)
scanf("%lld%lld%lld%lld", &a[i].x, &a[i].y, &a[i].g, &a[i].s);
sort(a+1, a+1+m, cmp);
for(long long i=1; i<=m; i++)
{
long long j;
for(j=1; j<=n; j++)
fa[j]=j;
for(j=top; j>=1; j--)
if(a[st[j]].s>a[i].s)
st[j+1]=st[j];
else break;
top++;
st[j+1]=i;
long long maxg=0, maxs=0, sum=0;
for(j=1; j<=top; j++)
{
long long fx=find(a[st[j]].x), fy=find(a[st[j]].y);
if(fx!=fy)
{
fa[fy]=fx;
maxg=max(a[st[j]].g, maxg);
maxs=max(a[st[j]].s, maxs);
st[++sum]=st[j];
}
}
if(sum==n-1)
ans=min(ans, wG*maxg+wS*maxs);
top=sum;
}
if(ans==1e18)
printf("-1");
else
printf("%lld", ans);
return 0;
}P6最佳买入
直接把个数拆成二次幂的和,然后做01背包
代码就不给了!