首页 > 其他 > 详细

9.26 考试总结

时间:2020-09-26 22:56:04      阅读:48      评论:0      收藏:0      [点我收藏+]

前言:

自己考试的时候,又犯了低级错误,导致到手的 100 分直接丢了。哎,蓝瘦。

T1 购物

【问题描述】

每一个物品有 \(a_i,b_i,c_i\) 三种属性,在每一层有 \(n\) 种物品。每一种在 第 \(x\) 层的代价为 \(a_i \times x^2 + b_i \times x + c_i\)

每一层的代价总和为这 \(n\) 中物品的代价之和,问你 \(1-F\) 这些层中总代价最少的那一层是第几层。

考试的时候打了个三分,结果就得了 \(50\) 分的暴力分。

快考完了才发现这是个 \(SB\) 数学题,然后没时间写了,只能交了三分的解法。

关键是他 \(T\) 的范围很小,我当时怀疑这种 \(O(T)\) 的解法不对。

推一下柿子,我们要求的是:

\(a_1\times x^2 + b_1\times x + c_1 + a_2\times x^2 + b_2 \times x + c_2 \cdots a_n \times x^2 + b_n \times x + c_n\)

然后合并一下同类项可得:

\(\displaystyle\sum_{i=1}^{n} a_i \times x^2 \sum_{i=1}^{n} b_i \times x + \sum_{i=1}^{n} c_o\)

然后你就会发现他还是个二次函数,求一下这个函数的对称轴就可以。

Code

#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
#define int long long
const int N = 1e5+10;
int T,n,F,ans,id,a[N],b[N],c[N];
inline int read()
{
	int s = 0,w = 1; char ch = getchar();
	while(ch < ‘0‘ || ch > ‘9‘){if(ch == ‘-‘) w = -1; ch = getchar();}
	while(ch >= ‘0‘ && ch <= ‘9‘){s = s * 10 + ch - ‘0‘; ch = getchar();}
	return s * w;
}
int calc(int x)
{
	int res = 0;
	return x * x * a[n] + x * b[n] + c[n];
}
signed main()
{
	//freopen("shopping.in","r",stdin);
	//freopen("shopping.out","w",stdout);
	T = read();
	while(T--)
	{
		n = read(); F = read(); ans = 0;
		for(int i = 1; i <= n; i++)
		{
			a[i] = read(); b[i] = read(); c[i] = read();	
		}
		for(int i = 1; i <= n; i++)
		{
			a[i] = a[i-1] + a[i];//前缀和
			b[i] = b[i-1] + b[i];
			c[i] = c[i-1] + c[i];
		}
		int ans = -1 * b[n] / (2 * a[n]);//二次函数对称轴
		if(ans <= 0) ans = 1;//对称轴小于0,说明1是最小的
		if(ans >= F) ans = F;//大于F的话,取 F 是最小的
		else if(ans >= 1 && ans <= F)
		{
			int k = ans + 1;
			if(calc(k) < calc(ans)) ans = k;//最后特判一下取 k 小还是 k+1 小
		}
		printf("%lld\n",ans);
	}
	fclose(stdin); fclose(stdout);
	return 0;
}

T2 加减法

给你一个 \(n\) 个点 \(m\) 条边的无向图,每个点有一个权值 \(a_i\) 满足 \(a_i \in [1-L]\) ,每次操作你都可以让相邻的两个点 \(a_u + 1, a_v-1\)

你可以进行任意多次操作,但最后要满足 \(a_i\in[1-L]\) ,问你最后 \(\displaystyle\sum_{i=1}^{n} i \times a_i\) 的值最大是多少。

一个贪心的想法就是尽可能的让 编号大的点的权值最大,并且每个联通块的答案是互不影响的。

我们只需要处理的就是每个联通块中的情况。

联通块里面的点因为是互相联通的,所以点权是可以互相转化的。

那我们可以对每个联通块开个 \(vector\) 存这个块里面的节点编号,然后从大到小排一遍序。

贪心的分配一下每个点的权值就行。

然后考场上犯了个 SB错误,这题直接炸了。

Code

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<vector>
using namespace std;
const int N = 1e5+10;
int n,m,L,u,v,top,num,cnt,tot;
int head[N],w[N],a[N];
vector<int> c[N];
bool vis[N];
long long ans;
inline int read()
{
	int s = 0,w = 1; char ch = getchar();
	while(ch < ‘0‘ || ch > ‘9‘){if(ch == ‘-‘) w = -1; ch = getchar();}
	while(ch >= ‘0‘ && ch <= ‘9‘){s = s * 10 + ch - ‘0‘; ch = getchar();}
	return s * w;
}
struct node
{
	int to,net;
}e[400010];
void add(int x,int y)
{
	e[++tot].to = y;
	e[tot].net = head[x];
	head[x] = tot;
}
void dfs(int x,int cnt)
{
	vis[x] = 1; w[cnt] += a[x]; 
	c[cnt].push_back(x);
	for(int i = head[x]; i; i = e[i].net)
	{	
		int to = e[i].to;
		if(vis[to]) continue;
		dfs(to,cnt);
	}
}
void slove(int x)
{
	sort(c[x].begin(),c[x].end());//先按编号从大到小排一遍序
	int t = c[x].size();
	for(int i = t-1; i >= 0; i--)
	{
		int to = c[x][i];
		a[to] = 1;//确保每个点的点权最小为1
	}
	w[x] -= t;
	for(int i = t-1; i >= 0; i--)//从编号大的点先分配开始
	{
		int to = c[x][i];
		a[to] += min(w[x],L-a[to]);
		w[x] -= a[to] - 1;
		if(w[x] <= 0) break;
	}
}
int main()
{
	freopen("pmlaw.in","r",stdin);
	freopen("pmlaw.out","w",stdout);
	n = read(); m = read(); L = read();
	for(int i = 1; i <= n; i++) a[i] = read();
	for(int i = 1; i <= m; i++)
	{
		u = read(); v = read();
		add(u,v); add(v,u);
	}
	for(int i = 1; i <= n; i++)//dfs找联通块
	{
		if(!vis[i]) cnt++, dfs(i,cnt); 
	}	
	for(int i = 1; i <= cnt; i++) slove(i);//解决每个联通块的情况
	for(int i = 1; i <= n; i++) ans += 1LL * a[i] * i;
	printf("%lld\n",ans);
	fclose(stdin); fclose(stdout);
	return 0;
}

T3 是个神仙数学题,暂时还不会。

9.26 考试总结

原文:https://www.cnblogs.com/genshy/p/13737129.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!