其他分享
首页 > 其他分享> > 9.26训练赛

9.26训练赛

作者:互联网

传送门

A

B

题意:有\(n\)个糖果,分为\(m\)种。现在让你从中选取糖果,使得\(\frac{糖果总价值}{所取糖果中某种糖果最大数量}\)这个比值最大。规定如果取第\(i\)种糖果,那就至少取\(l_i\)个。

注意,同种糖果价值可能不同。

思路:如果取第一种糖果,那就至少取\(l_1\)个,显然按价值从大到小取可以使得分子更大。

对每种糖果从大到小排序,然后维护前缀和。显然在糖果数量到达\(l_i\)的之前,对答案的贡献为0,因此\(l_i\)之前的前缀和为0,直到\(l_i\)才有所贡献。

这里需要注意某种糖果是至少取\(l_i\)个,而不是只取\(l_i\)个,因此后续\(l_i\)后的前缀和需要继续更新。

#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<vector>
using namespace std;
const int MAXN = 100009;
typedef long long ll;
ll n,m;
ll l[MAXN],sum[MAXN];
ll gcd(ll a,ll b){return !b ? a : gcd(b,a % b);}
bool cmp(ll a,ll b){return a > b;}
vector<ll>G[MAXN];
void solve(){
	ll T;
	cin >> T;
	while(T --){
		memset(sum,0,sizeof(sum));
		ll Max = 0,u = 1,v = 1;
		scanf("%lld%lld",&n,&m);
		for(int i = 1;i <= m;i ++) scanf("%lld",&l[i]),G[i].push_back(0);
		for(int i = 1;i <= n;i ++){
			ll a,b;
			scanf("%lld%lld",&a,&b);
			G[b].push_back(a);
		}
		for(int i = 1;i <= m;i ++){
			sort(G[i].begin() + 1,G[i].end(),cmp);//每种糖果价值从大到小排序 
			ll num = 0;
			Max = max(Max,l[i]);//更新最大数量,可以证明最大数量等于最大的L 
			for(int j = 1;j < G[i].size();j ++){
				if(j < l[i]) num += G[i][j];//Li之前第i种糖果的贡献为0 
				else if(j == l[i]) sum[j] += num + G[i][j];//此时糖果开始有贡献。 
				else sum[j] += G[i][j];
			}
			G[i].clear();//因为是多组数据,所以别忘记初始化 
		}
		for(int i = 1;i <= Max;i ++) sum[i] += sum[i - 1];//计算前缀和,由于每种糖果不一定只取Li个,所以要把每个数量都要更新。 
		sort(l + 1,l + m + 1);
		for(int i = 1;i <= m;i ++){//由于最大数量一定是Li,所以直接枚举Li即可 
			if(sum[l[i]] * v > u * l[i])
				u = sum[l[i]],v = l[i];
		}
		cout << u / gcd(u,v) << "/" << v / gcd(u,v) << endl;
	}
}


int main(){
	solve();
	return 0;
} 

C

D

E

F

G

思路:因为\(x,y\)轴上的移动是独立的,所以可以把二维化成一维考虑。在数轴上,\(n\)条线段的端点重合于某点\(x\)。每条线段移动距离为\(min\left ( |l - x|,|r - x|\right )\),即移动距离x近的端点。记此端点为\(m_i\),则答案\(sum = |m1 - x| + |m2 - x| + ...+|mn - x|\)。当x为中位数时,\(sum\)最小(可以画图证明)。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;

const int MAXN = 2000001;
int T,n;
int x[MAXN],y[MAXN];

struct hh{
	int a,b,c,d;
}ma[MAXN];


void solve(){
	cin >> T;
	int a,b,c,d;
	while(T --){
		int X,Y,tot1 = 0,tot2 = 0;
		scanf("%d",&n);
		for(int i = 1;i <= n;i ++){
			scanf("%d%d%d%d",&a,&b,&c,&d);
			x[++ tot1] = a,x[++ tot1] = c;
			y[++ tot2] = b,y[++ tot2] = d;
			ma[i] = (hh){a,b,c,d};
		}
		sort(x + 1,x + tot1 + 1);
		sort(y + 1,y + tot2 + 1);
		X = x[n],Y = y[n];
		long long sum = 0;
		for(int i = 1;i <= n;i ++){
			if(ma[i].a > X || ma[i].c < X) sum += min(abs(ma[i].a - X),abs(ma[i].c - X));
			if(ma[i].b > Y || ma[i].d < Y) sum += min(abs(ma[i].b - Y),abs(ma[i].d - Y));
		}
		cout << sum << "\n";
	}
	return;
}


int main(){
	solve();
	return 0;
}

H

思路:典型的贪心,不难,搞懂三个数组就可以了。

\(a\) 1 3 1 4 5

\(b\) 1 2 -2 3 1

\(c\) 1 2 0 3 1

\(b[i] = a[i] - a[ i - 1]\)

\(b[i] > 0,c[i] = b[i]; b[i] < 0,c[i] = 0;\)
可以明显的看出,\(c\)的前缀和就是最少的操作次数,\(b\)的前缀和是\(a\)。

开两个线段树,一个维护\(b\),另一个维护\(c\);

线段树基本操作,单点修改,区间求和。

单点修改,只需要\(b[l]\)加\(k\),\(b[r + 1]\)减\(k\)即可。

区间求合,答案显然是\(b[1 : l] + c[l + 1 : r]\)

#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
using namespace std;

const int MAXN = 1000001;
typedef long long ll;
struct hh{
	int l,r;
	ll sum;
}tree[MAXN][2];
int a[MAXN],b[MAXN],c[MAXN];

void build(int now,int l,int r,int f){
	tree[now][f].l = l,tree[now][f].r = r;
	if(l == r){
		tree[l][f].sum = 0;
		return;
	}
	int mid = (l + r) >> 1;
	build(now << 1,l,mid,f);
	build(now << 1 | 1,mid + 1,r,f);
}

void up(int now,int pos,int x,int f){
	if(tree[now][f].l == tree[now][f].r){
		tree[now][f].sum = x;
		return;
	}
	int mid = (tree[now][f].l + tree[now][f].r) >> 1;
	if(pos <= mid) up(now << 1,pos,x,f);
	else if(pos >= mid + 1) up(now << 1 | 1,pos,x,f);
	tree[now][f].sum = tree[now << 1][f].sum + tree[now << 1 | 1][f].sum;
	return;
}

ll query(int now,int x,int y,int f){
	ll ans = 0;
	if(tree[now][f].l >= x && tree[now][f].r <= y) return tree[now][f].sum;
	int mid = (tree[now][f].l + tree[now][f].r) >> 1;
	if(x <= mid) ans += query(now << 1,x,y,f);
	if(y >= mid + 1) ans += query(now << 1 | 1,x,y,f);
	return ans;
}

void solve(){
	int T,n,m;
	cin >> T;
	while(T --){
		scanf("%d%d",&n,&m);
		build(1,1,n,0),build(1,1,n,1);
		a[0] = 0;
		for(int i = 1;i <= n;i ++){
			scanf("%d",&a[i]);
			b[i] = a[i] - a[i - 1];
			c[i] = b[i] > 0 ? b[i] : 0;
			up(1,i,b[i],0),up(1,i,c[i],1);
		}
		for(int i = 1;i <= m;i ++){
			int op,l,r,k;
			scanf("%d%d%d",&op,&l,&r);
			if(op == 1){
				scanf("%d",&k);
				b[l] += k,b[r + 1] -=k;
				c[l] = b[l] > 0 ? b[l] : 0;
				c[r + 1] = b[r + 1] > 0 ? b[r + 1] : 0;
				up(1,l,b[l],0),up(1,l,c[l],1);
				if(r + 1 <= n) 
					up(1,r + 1,b[r + 1],0),up(1,r + 1,c[r + 1],1);
			}
			else{
				if(l != r) cout << query(1,1,l,0) + query(1,l + 1,r,1) << endl;
				else cout << query(1,1,l,0) << endl;
			}	
		}
	}
	return;
}

int main(){
	solve();
	return 0;
}

I

J

标签:9.26,int,ll,MAXN,训练赛,include,糖果,sum
来源: https://www.cnblogs.com/cgold/p/13740045.html