The 13th Chinese Northeast Collegiate Programming Contest 部分题解

B. Balanced Diet

题意:

商店有 m m m n n n个糖果,每个糖果有一个权值,现在你要买一些糖果使得 S c \frac{S}{c} cS最大,其中 S S S为你购买的糖果的权值和, c c c为你购买的出现次数最多的那种糖果出现的种类数,但是每种糖果的购买数量不能在 [ 1 , l i ) [1,l_i) [1,li)之间。

思路:

枚举 c c c,利用前缀和求得购买的每种糖果的权值和,更新最大值。

代码:

#include<bits/stdc++.h>

using namespace std;
const int N = 1e6+100;
typedef long long ll;
ll L[N];
vector<ll> G[N],sum[N];
bool cmp(ll a,ll b){
	return a>b;
}
int main(){
	ll T;
	scanf("%I64d",&T);
	while(T--){
		ll n,m;
		scanf("%I64d %I64d",&n,&m);
		for(ll i=1;i<=m;i++) G[i].clear(),sum[i].clear(); 
		for(ll i=1;i<=m;i++) scanf("%I64d",&L[i]);
		ll lm=0;
		for(ll i=1;i<=n;i++){
			ll a,b;
			scanf("%I64d %I64d",&a,&b);
			G[b].push_back(a);
			lm=max(lm,(ll)G[b].size());
		}
		for(ll i=1;i<=m;i++){
			sort(G[i].begin(),G[i].end(),cmp);
			if(G[i].size()) sum[i].push_back(G[i][0]);
			for(ll j=1;j<(ll)G[i].size();j++){
				sum[i].push_back(sum[i][j-1]+G[i][j]);
			}
		}
		double ans=0;
		ll maxu,maxv;
		for(ll x=1;x<=lm;x++){
			ll u=0;
			for(ll i=1;i<=m;i++){
				if(G[i].size()<L[i]||x<L[i]) continue;
				u+=sum[i][min((ll)G[i].size(),x)-1];
			}
			if(u*1.0/x>ans){
				ans=u*1.0/x;
				maxu=u;
				maxv=x;
			}
		}
		ll cc=__gcd(maxu,maxv);
		printf("%I64d/%I64d\n",maxu/cc,maxv/cc);
	}
	return 0;
}

C. Line-line Intersection

题意:

给你一组直线,求有多少对直线之间至少有一个交点,其中重叠的也应被计算。

思路:

直线方程可以写成 ( x 1 − x 2 ) ∗ y = ( y 1 − y 2 ) ∗ x + ( x 1 − x 2 ) ∗ y 1 − ( y 1 − y 2 ) ∗ x 1 (x1-x2)*y=(y1-y2)*x+(x1-x2)*y1-(y1-y2)*x1 (x1x2)y=(y1y2)x+(x1x2)y1(y1y2)x1的形式。
利用 g c d gcd gcd简化,然后累计当前个数,每次答案加上当前线段个数减去斜率不同的以及重叠即可。
复杂度 O ( n l o g ( n ) ) O(nlog(n)) O(nlog(n))

代码:

#include<bits/stdc++.h>

using namespace std;
const int N = 1e6+100;
int main(){
	int T;
	scanf("%d",&T);
	while(T--){
		map<pair<long long,long long>,multiset<long long> >line;
		map<pair<pair<int,int>,int>,int> num;
		int n;
		scanf("%d",&n);
		int tot=0;
		long long  xa,ya,xb,yb;
		long long ans=0;
		for(int i=1;i<=n;i++){
			scanf("%I64d %I64d %I64d %I64d",&xa,&ya,&xb,&yb);
			long long dx=xa-xb;
			long long dy=ya-yb;
			long long c=__gcd(dx,dy);
			dx/=c;dy/=c;
			if(dx<0&&dy<0){
				dx=-dx;
				dy=-dy;
			}
			long long z=ya*dx-xa*dy;
			pair<long long,long long> nowk;
			nowk.first=dx;nowk.second=dy;
			num[{nowk,z}]++;
			line[nowk].insert(z);
			tot++;
			ans+=tot-(long long)line[nowk].size()+(long long)num[{nowk,z}]-1;
		}
		printf("%I64d\n",ans);
	}
	return 0;
}

E. Minimum Spanning Tree

题意:

一棵树,把边变成点,点变成边,重构成一个边图(邻边之间由权值为两边和),求边图的MST。

思路:

暴力是不可行的,因为重构图边的数量可以达到 n 2 n^2 n2级别,因此我们考虑计算树上每个点的贡献。
树上每个点在新构成的边图中是一个,而对于团的最小生成树,显然是每个边向边权最小的连接。复杂度 O ( n ) O(n) O(n)

代码:

#include<bits/stdc++.h>

using namespace std;
const int N = 1e5+100;
typedef long long ll;
const ll INF = 0x3f3f3f3f;
ll minn[N];
ll sum[N];
ll ind[N];
int main(){
	int T;
	scanf("%d",&T);
	while(T--){
		int n;
		scanf("%d",&n);
		ll u,v,w;
		memset(minn,INF,sizeof(minn));
		memset(sum,0,sizeof(sum));
		memset(ind,0,sizeof(ind));
		for(int i=1;i<=n-1;i++){
			scanf("%I64d %I64d %I64d",&u,&v,&w);
			ind[u]++;
			ind[v]++;
			sum[u]+=w;
			minn[u]=min(minn[u],w);
			sum[v]+=w;
			minn[v]=min(minn[v],w);			
		}
		ll ans=0;
		for(int i=1;i<=n;i++){
			if(ind[i]>=2){
				ans+=(sum[i]+(ind[i]-2)*minn[i]);
			}
		}
		printf("%I64d\n",ans);
	}
	return 0;
}

G. Radar Scanner

题意:

n n n个矩阵,可以对每个矩阵执行上下左右移动一步的操作,求最少移动多少次使得这些矩阵至少有一块小方格被全部矩阵覆盖。

思路:

矩阵的二维是相互独立的,即上下只能影响纵坐标,左右只能影响横坐标。因此我们转化为一维问题来解决。
线段 [ l , r ] [l,r] [l,r]移动到点 x x x的代价为 ∣ l − x ∣ + ∣ r − x ∣ − ∣ r − l ∣ 2 \frac{|l-x|+|r-x|-|r-l|}{2} 2lx+rxrl,最终我们要找到一个点 x x x使得 ∑ ∣ l − x ∣ + ∣ r − x ∣ \sum{|l-x|+|r-x|} lx+rx最小,显然 x x x是所有 l , r l,r l,r的中位数。计算即可。复杂度 O ( n ) O(n) O(n)

#include<bits/stdc++.h>

using namespace std;
typedef long long ll;
const ll N = 1e6+100;
struct line{
	ll l,r;
};
vector<line> A,B;//横纵 
vector<ll> ALLA,ALLB;
int main(){
	ll T;
	scanf("%I64d",&T);
	while(T--){
		A.clear();B.clear();
		ALLA.clear();ALLB.clear();
		ll n;
		scanf("%I64d",&n);
		ll a,b,c,d;
		for(ll i=1;i<=n;i++){
			scanf("%I64d %I64d %I64d %I64d",&a,&b,&c,&d);
			A.push_back({a,c});B.push_back({b,d});
			ALLA.push_back(a);ALLA.push_back(c);
			ALLB.push_back(b);ALLB.push_back(d);
		}
		sort(ALLA.begin(),ALLA.end());
		sort(ALLB.begin(),ALLB.end());
		ll siza=ALLA.size();
		ll x=(ALLA[siza/2-1]+ALLA[siza/2])/2;
		ll y=(ALLB[siza/2-1]+ALLB[siza/2])/2;
		long long ans=0;
		for(auto sig:A){
			ans+=(abs(sig.l-x)+abs(sig.r-x)-(sig.r-sig.l))/2;
		}
		for(auto sig:B){
			ans+=(abs(sig.l-y)+abs(sig.r-y)-(sig.r-sig.l))/2;
		}
		printf("%I64d\n",ans);
	}
	return 0;
} 

J. Time Limit

思路:

按要求模拟即可。

#include<bits/stdc++.h>

using namespace std;
const int N = 1e6+100;
typedef long long ll;
int a[N];
int main(){
	int T;
	scanf("%d",&T);
	while(T--){
		int n;
		scanf("%d",&n);
		for(int i=1;i<=n;i++) scanf("%d",&a[i]);
		int ans=a[1]*3;
		for(int i=2;i<=n;i++) ans=max(ans,a[i]+1);
		if(ans&1) ans++;
		printf("%d\n",ans);
	} 
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值