2018 CCPC网络赛 补题

这篇博客介绍了2018年CCPC网络赛的补题,包括使用优先队列解决"Buy and Resell"问题,通过计算利润和交易次数寻求最大收益;探讨了"Tree and Permutation"题目的排列距离和计算,利用贡献思想和插空法求解;以及关于数学问题"Dream"的解析,涉及重新构造加法和乘法以满足特定条件。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

 Buy and Resell(优先队列)

题目链接

题意:按顺序走过N个城市,每个城市有对应的货物价值,可以买入或者卖出,求最多利润及获得最大利润的最短交易次数。

题解:我们假设在每一个城市都买入,维护一个升序的优先队列,里面标识的是买入的货物价值,所以每次push进去,并枚举每个城市的货物价值,如果大于队列队首元素,则给结果加上差值(记作x1),并次数加一,在这里需要考虑到可能后面有货物价值大于此次枚举的值,所以要再将这次枚举的值push进去,等到它为队首且小于新枚举的值时,结果加上差值(记作x2),这时可以想到x1 + x2的值就等于x1那次没有卖出,在这次再卖出货物的价值,所以,次数减一。

优先对列,map

参考自大牛博文:https://blog.youkuaiyun.com/Tony5t4rk/article/details/82056768

代码如下:

#include<cstdio>
#include<functional>
#include<queue>
#include<vector>
#include<map>
using namespace std;
int main()
{
	int t, n, x;
	scanf("%d",&t);
	while(t--)
	{
		long long ans = 0;
		int time = 0;
		priority_queue<int, vector<int>, greater<int> > p; //升序 
		map<int, int> vis;
		scanf("%d",&n);
		while(n--)
		{
			scanf("%d",&x);
			p.push(x);
			if(p.top() < x)
			{
				time ++;
				ans += x - p.top();
				if(vis[p.top()])
				{
					time --;
					vis[p.top()] --;
				}
				vis[x] ++;
				p.pop();
				p.push(x);
			}
		} 
		printf("%lld %d\n",ans, time * 2);
	} 
	return 0;
}

Tree and Permutation:

题目链接

题意:N!中排列求出距离和,距离定义为:假设一个排列为a1,a2,a3,则距离就是a1a2+a2a3,最后对所有排列求和。

解析:贡献思想,选中其中一边,则边的左边是a个结点,则右边是b个结点,那么左边和右边连接的方式就有a*b种,左右交换又是b*a,然后除去这条边的两个节点之外,剩下(n-2)个节点构成(n-2)!种排列,有(n-1)个空,插空法,那么结果就是2*a*(n-a)*len*(n-2)!*(n-1)。

代码如下:

#include<cstdio>
#include<cstring>
#define ll long long
using namespace std;
const int maxn = 1e5 + 10;
const ll mod = 1e9 + 7;
int n, cnt;
int head[maxn], num[maxn];
ll dp[maxn], fac[maxn];
ll ans;
struct node
{
	int to, next;
	ll w;
}edge[maxn * 2];
void add(int u, int v, ll w)
{
	++ cnt;
	edge[cnt].to = v;
	edge[cnt].next = head[u];
	edge[cnt].w = w;
	head[u] = cnt;
} 
void getfac()
{
	fac[0] = 1;
	fac[1] = 1;
	for(int i = 2; i < maxn; ++ i)
	{
		fac[i] = i * fac[i-1] % mod;
	}
}
void dfs(int u, int fa)
{
	dp[u] = 1;
	for(int i = head[u]; i ;i = edge[i].next)
	{
		int v = edge[i].to;
		if(v == fa) continue;
		dfs(v, u);
		dp[u] += dp[v];
		ans = (ans + dp[v] * (n - dp[v]) % mod * edge[i].w % mod) % mod; 
	}
}
int main()
{
	getfac();
	while(~scanf("%d",&n))
	{
		memset(num, 0, sizeof(num));
		memset(head, 0, sizeof(head));
		memset(dp, 0, sizeof(dp));
		cnt = 0;
		for(int i = 0; i < n - 1; ++ i)
		{
			int l, r;
			ll w;
			scanf("%d%d%lld",&l,&r,&w);
			num[l] ++; num[r] ++;
			add(l, r, w);
			add(r, l, w);
		} 
		ans = 0;
		for(int i = 1; i <= n; ++ i)
		{
			if(num[i] == 1)
			{
				dfs(i, 0);
				break;
			}
		}
		ans = ans * 2 % mod * fac[n-1] % mod;
		printf("%lld\n",ans); 
	} 
	return 0;
}

Dream

题目链接

题意:重新构造加法和乘法,使当p为质数时(m+n)^p = m^p + n^p 成立。输出

解析:

代码如下:

#include<cstdio>
using namespace std;
int main()
{
	int t, p;
	scanf("%d",&t);
	while(t--)
	{
		scanf("%d",&p);
		for(int i = 0; i < p; ++ i)
		{
			for(int j = 0; j < p; ++ j)
			{
				printf("%d ", (i + j) % p);
			}
			printf("\n");
		}
		for(int i = 0; i < p; ++ i)
		{
			for(int j = 0; j < p; ++ j)
			{
				printf("%d ", (i * j) % p);
			}
			printf("\n"); 
		}
	}
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值