The 13th Chinese Northeast Collegiate Programming Contest
题目列表
- B - Balanced Diet
- C - Line-line Intersection
- E - Minimum Spanning Tree
- G - Radar Scanner
- H - Skyscraper
- J - Time Limit
Practice link:https://vjudge.net/contest/392938#overview
B - Balanced Diet
题意: 给你m种食物,给你1~m个L[i]表示如果你要取第i种食物,你至少要取L[i]个。再给你n个食物,有n个a[i]和b[i]分别表示食物的价值和种类,设U为食物的总价值,V为所有种类的食物中数量最大的种类的数量,问U/V的最大值是多少。
思路:首先我们看取某种食物的顺序,因为U越大越好,因此每类食物都要从大到小排序,然后枚举每个分母V的值,那么分母U的值也就是在符合L[i]的前提下取第i种食物的前V项即可。
代码:
‘’‘’‘’
const int maxn = 100005;
inline ll gcd(ll a,ll b) {
return b>0 ? gcd(b,a%b):a;
}
struct node{
int l,id;
}ss[maxn];
int n,m;
vector<int>swt[maxn];
vector<int>sum[maxn];
bool cmp(int x,int y)
{
return x>y;
}
int main()
{
int T;
cin>>T;
while(T--){
scanf("%d %d",&n,&m);
for(int i=1;i<=max(n,m);i++){
swt[i].clear();
sum[i].clear();
}
for(int i=1;i<=m;i++){
scanf("%d",&ss[i].l);
ss[i].id=i;
}
for(int i=1;i<=n;i++){
int x,y;
scanf("%d %d",&x,&y);
swt[y].push_back(x);
}
for(int i=1;i<=m;i++){
sort(swt[i].begin(),swt[i].end(),cmp);
for(int j=0;j<swt[i].size();j++){
sum[max(j+1,ss[i].l)].push_back(swt[i][j]);
}
}
ll mm=0,zz=0,nm=1,nz=0;
for(int i=1;i<=n;i++){
mm=i;
for(int j=0;j<sum[i].size();j++){
zz+=sum[i][j];
}
f(1ll*nm*zz>1ll*nz*mm){
nm=mm;
nz=zz;
}
}
ll pp=gcd(nz,nm);
printf("%lld/%lld\n",nz/pp,nm/pp);
}
return 0;
}
C - Line-line Intersection
思路:两条直线不平行则必相交,若平行:若相交那么答案+1,否则不算。两个map去储存,第一个map储存某一斜率下平行的数量,第二个map储存某一条直线的重合数量。在添加第i条直线时,答案ans=i-平行数量+重合数量。
代码:
‘’‘’‘’
map<pair<ll,ll>,ll>k;
map<pair<pair<ll,ll>,ll>,ll>c;
int main()
{
int n,t;
t=read();
while(t--)
{
n=read();
ll ans=0;
k.clear();
c.clear();
for(int i=0;i<n;i++)
{
ll x1,y1,x2,y2,x;
scanf("%lld%lld%lld%lld",&x1,&y1,&x2,&y2);
x=x1*y2-x2*y1;
ll xx=x2-x1;
ll yy=y2-y1;
ll d=__gcd(xx,yy);
xx/=d;
yy/=d;
x/=d;
ans+=i-k[{xx,yy}]+c[{{xx,yy},x}];
k[{xx,yy}]++;
c[{{xx,yy},x}]++;
}
printf("%lld\n",ans);
}
return 0;
}
E - Minimum Spanning Tree
题意:给你一幅图,把边看成点,有公共点的两条边就是有一条连边的两个点,边权就是两条边的边权之和,让你构造一颗最小生成树,问你边权之和是多少。
思路:我们从每条边的贡献入手,显然,一个点的所有出边中,我们要用到最多的就是最小边,最小边要和可以和它相连的所有边相连,则其贡献就是(出边数-1)*w,对于其它边,要保证连通,都会用到一次。
代码:
vector<ll>g[maxn];
int main()
{
ll T,n;
cin>>T;
while(T--){
scanf("%lld",&n);
for(int i=1;i<=n;i++){
g[i].clear();
}
for(int i=1;i<n;i++){
ll u,v,w;
scanf("%lld %lld %lld",&u,&v,&w);
g[u].push_back(w);
g[v].push_back(w);
}
ll ans=0;
ll res=0;
ll minn=INF;
for(int i=1;i<=n;i++){
res=0;
minn=INF;
if(g[i].size()<=1)continue;
for(int j=0;j<g[i].size();j++){
ll u=g[i][j];
res+=u;
minn=min(minn,u);
}
res+=(g[i].size()-2)*minn;
ans+=res;
}
printf("%lld\n",ans);
}
return 0;
}
G - Radar Scanner
题意:在二维平面上,给你n个矩形的左下角和右上角坐标,一次移动可以选择一个矩形向上下左右移动一格。问你最少多少次移动可以让所有矩形有一个公共相交点。
思路:首先是二维平面上的问题,且相互独立,那么可以转化为一维问题。就是若干条直线,最少移动多少次有公共点。猜一下是移到所有点的中位数就是最小。
代码:
‘’‘’‘’
const int maxn = 100005;
vector<ll>g[maxn];
int main()
{
ll T,n;
cin>>T;
while(T--){
scanf("%lld",&n);
for(int i=1;i<=n;i++){
g[i].clear();
}
for(int i=1;i<n;i++){
ll u,v,w;
scanf("%lld %lld %lld",&u,&v,&w);
g[u].push_back(w);
g[v].push_back(w);
}
ll ans=0;
ll res=0;
ll minn=INF;
for(int i=1;i<=n;i++){
res=0;
minn=INF;
if(g[i].size()<=1)continue;
for(int j=0;j<g[i].size();j++){
ll u=g[i][j];
res+=u;
minn=min(minn,u);
}
res+=(g[i].size()-2)*minn;
ans+=res;
}
printf("%lld\n",ans);
}
return 0;
}
H - Skyscraper
题意:n座摩天大楼,每座的预计层高为a[i]。
m次操作:
1、将a的某个区间[l , r]加上k。
2、询问仅对[l, r]区间从0开始施工,最少需 要几个阶段。
每一个阶段操作,可以任意选取一个区间,在这个区间上每个a[i]加上1 。
思路:
** 1、首先令b[I]=a[I]-a[i-1] 。
** 2、b[i]>0,那么要多花b[i]个阶段才能把a[i]修好。
** 3、b[i]<=0,那么在修a[i-1]时就可以把a[i]顺带着修好。
** 4、那么令c[I]=b[i] (b[ I]>0),就表示在修好i-1这个楼后要修i这个楼需要的阶段。
** 5、那么对于区间[l,r]需要的阶段就是a[l]+c[l+1]+c[l+2]+……+c[r-1]+c[r]。
** 6、也就是(b[1]+b[2]+……+b[l])+(c[l+1]+c[l+2]+……+c[r-1]+c[r])。
** 7、对于区间加的操作,我们只需要在b和c数组的l处加上k,r+1处减去k,即可。
** 8、用树状数组维护前缀和即可。
代码:
‘’‘’‘’
const int maxn = 100005;
ll a[maxn],b[maxn],c[maxn];
ll T,n,m;
struct tr{
ll sum[maxn];
void add(int p,ll x){
while(p<=n){
sum[p]+=x;
p+=p&(-p);
}
}
ll ask(int p){
ll res=0;
while(p){
res+=sum[p];
p-=p&(-p);
}
return res;
}
ll r_ask(int l,int r){
return ask(r)-ask(l-1);
}
}tb,tc;
int main()
{
cin>>T;
while(T--){
for(int i=1;i<maxn;i++){
tb.sum[i]=0;
tc.sum[i]=0;
b[i]=0;
}
scanf("%lld %lld",&n,&m);
for(int i=1;i<=n;i++){
scanf("%lld",&a[i]);
if(i>=2){
b[i]=a[i]-a[i-1];
tb.add(i, b[i]);
if(b[i]>0){
tc.add(i, b[i]);
}
}else{
b[i]=a[i];
tb.add(i, a[i]);
tc.add(i, a[i]);
}
}
int op,l,r,x;
while(m--){
scanf("%d",&op);
if(op==1){
scanf("%d %d %d",&l,&r,&x);
tb.add(l, x);
tb.add(r+1,-x);
if(b[l]>0){
tc.add(l, x);
}else if(b[l]+x>0){
tc.add(l, b[l]+x);
}
if(b[r+1]>0){
if(b[r+1]-x>0){
tc.add(r+1, -x);
}else{
tc.add(r+1, -b[r+1]);
}
}
b[l]+=x;
b[r+1]-=x;
}else{
scanf("%d %d",&l,&r);
printf("%lld\n",tb.r_ask(1,l)+tc.r_ask(l+1,r));
}
}
}
return 0;
}
J - Time Limit
代码:
‘’‘’‘’
#include<bits/stdc++.h>
using namespace std;
int t,a[1001],n;
int main()
{
scanf("%d",&t);
while(t--)
{
scanf("%d",&n);
for(int i=1;i<=n;i++) scanf("%d",&a[i]);
int k=3*a[1];
for(int i=2;i<=n;i++)
{
k=max(k,1+a[i]);
}
if(k%2==0) printf("%d\n",k);
else printf("%d\n",k+1);
}
return 0;
}
记 2020 9.5 组队训练