The 2024 CCPC Shandong Invitational Contest and Provincial Collegiate Programming Contest

2024山东省赛个人记录

A:注意二分的函数要提前返回,不然会爆ll,同一次的错误不能在犯第二次了

#include<bits/stdc++.h>

using namespace std;

typedef long long ll;
const int N = 100100;


__int64 t[N], l[N], w[N];
__int64 n, k;

__int64 check(__int64 x)
{
	__int64 sum = 0;
	for(int i = 1; i <= n; i ++)
	{
		__int64 s = t[i] * l[i] + w[i];
		__int64 ans = (x / s) * l[i];
		if(x % s >= l[i] * t[i]) sum += l[i];
		else sum += (x % s) / t[i];
		sum += ans;
	}
	return sum;
}
int main()
{
	int T;
	cin >> T;
	while(T --){
		cin >> n >> k;
		for(int i = 1; i <= n; i ++)
		{
			cin >> t[i] >> l[i] >> w[i];
		}
		__int64 l = 0, r = 2e19;
        while(l + 1 < r){
			__int64 mid = (l + r) / 2;
			if(check(mid) < k) l = mid;
			else r = mid;
		}
		cout << r << endl;
	}
	return 0;
}

J:相当于最小生成树裸题吧,但是也不太好想的,,这里我们需要注意题目中的所有颜色的顶点时都需要用到的,有题目可以知道每个顶点都至少存在一个,因此我们可以先各取出来做一遍最小生成树,然后在将剩余的顶点数量 乘以 该顶点的最小的颜色即可,题意还是不太好想的,

#include<bits/stdc++.h>

using namespace std;

typedef long long ll;
const int N = 1e6 + 10;
typedef pair<ll, ll>PII;
const int MOD = 1e9 + 7;

//最小生成树

ll p[N];
int finds(int x)
{
	if(x != p[x]) p[x] = finds(p[x]);
	return p[x];
}
//kru最小生成树算法
struct node
{
	ll u, v, x;
	bool operator < (const node & w) const 
	{
		return x < w.x;//求最小的 
	};
	
}e[N];
ll minx[N];//记录最小点权

//最小生成树的性质,直接找最小边连在一起
//这道题一直没理解的原因就是不知道怎么连的
//就是先各取出来一条边相连,然后内部的点在进行相连,内部的点相连时只需要-1在乘上最小的点权即可
//实际上最小的点权就是第i行的最小值因为其他的点可以直接与其相连的
//确实不太好想的
int main()
{
	int t;
	cin >> t;
	while(t --){
		int n;
		cin >> n;
		vector<int>v(n + 10);
		for(int i = 1; i <= n; i ++)
		{
			cin >> v[i];
			p[i] = i;//进行初始化
			minx[i] = 99999999999;
		}
		int k = 0;
		for(int i = 1; i <= n; i ++)
		{
			for(int j = 1; j <= n; j ++)
			{
				ll x;
				cin >> x;
			    //有题知道每个顶点都至少存在一个的
				//if(i != j)
				e[++ k] = {i, j, x};
				minx[i] = min(minx[i], x);
			}
		}
		ll s = 0;
		//kru算法求最小生成树
		sort(e + 1, e + 1 + k);
		for(int i = 1; i <= k; i ++)
		{
			int a = finds(e[i].v);
			int b = finds(e[i].u);
			//a和b均是父亲
			if(p[a] != p[b])
			{
				p[a] = b;//两个的父亲合并//b作为父亲了,然后a的父亲变为了b也就相当于合并了
				s += e[i].x;
			}
		}
		for(int i = 1; i <= n; i ++)//加上内部的点
		{
			ll o = v[i] - 1;//一开始取出来一条边
			if(o <= 0) continue;
			s += minx[i] * o;
		}
		cout << s << endl;
	}
	return 0;
}

C:我们很容易想到树状数组 + 离散化的做法,也可以用优先队列来做,我们按照左端点进行排序, 然后维护右端点,当左端点大于右端点时,弹出,剩下的就是与它有重复区间的段的数量

#include<bits/stdc++.h>
using namespace std;
const int N = 5e5 + 5;
const int M = 1e9 + 7;
typedef long long ll;
typedef pair<ll,ll>PII;

const int MOD = 998244353;
PII p[N];
int main()
{
	int t;
	cin >> t;
	while(t --)
	{
		int n, k;
		cin >> n >> k;
		for(int i = 1; i <= n; i ++) 
		{
			cin >> p[i].first >> p[i].second;	
		}
		sort(p + 1, p + 1 + n);
		priority_queue<int, vector<int>, greater<int>>q;
		ll res = 1;
		for(int i = 1; i <= n; i ++)
		{
			if(i == 1)
			{
				res = res * k % MOD;
				k --;//重复就要少一种方案
				q.push(p[i].second);
			}
			else 
			{
				while(!q.empty() && q.top() < p[i].first)
				{
					k ++;
					q.pop();
				}//不重复就要增加一种方案的
				res = res * k % MOD;
				k --;
				if(k < 0) k = 0;
				q.push(p[i].second);
			}
		}
		cout << res << endl;
	}
	
	return 0;
}//好累啊没有状态
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值