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