开始: 2023-06-05 00:00:00

230605稠州PK赛

结束: 2023-06-08 00:00:00
当前  2025-01-24 16:30:01  类型: IOI  状态: 已经结束 

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背包

代码就不给了!