2019 东北四省赛部分题解 The 13th Chinese Northeast Collegiate Programming Contest

比赛链接 :https://codeforces.com/gym/102220

J题: 按题意模拟就行

#include<bits/stdc++.h>
#define bug1(g) cout<<"test: "<<g<<endl
#define bug2(g,i) cout<<"test: "<<g<<" "<<i<<endl
#define bug3(g,i,k) cout<<"test: "<<g<<" "<<i<<" "<<k<<endl
#define bug4(a,g,i,k) cout<<"test: "<<a<<" "<<g<<" "<<i<<" "<<k<<endl
using namespace std;
typedef long long ll;
ll n;
int t;
ll a[20];
ll x;
int main()
{
    ios::sync_with_stdio(0);
     cin>>t;
     while(t--){
        cin>>n;
        cin>>x;
        ll maxx=x*3;
       for (int i =2;i<=n;i++)
        {
            cin>>a[i];
            maxx=max(maxx,a[i]+1);
        }
        if(maxx&1) maxx++;
        cout<<maxx<<endl;

     }
    return 0;
}

G题: 给N个矩形,每个矩形每次可以上下左右移动一格,问至少几次可以使得有一个方格被所有矩形覆盖

按照出题人的做法
在这里插入图片描述证明在这 https://www.cnblogs.com/xidian-mao/p/7819928.html
代码如下

#include<bits/stdc++.h>
#define bug1(g) cout<<"test: "<<g<<endl
#define bug2(g,i) cout<<"test: "<<g<<" "<<i<<endl
#define bug3(g,i,k) cout<<"test: "<<g<<" "<<i<<" "<<k<<endl
#define bug4(a,g,i,k) cout<<"test: "<<a<<" "<<g<<" "<<i<<" "<<k<<endl
using namespace std;
typedef long long ll;
const int maxn=100005;
int n;
struct node
{
   int xl,xr,yl,yr;
}a[maxn];
int x[maxn*2],y[maxn*2];
int cntx,cnty;
ll sumx,sumy;
int t;
int main()
{
    //ios::sync_with_stdio(0);
     scanf("%d",&t);
     while(t--){
        scanf("%d",&n);
        cntx=cnty=1;
        sumx=sumy=0;
        for(int i =1;i<=n;i++)
        {
            scanf("%d %d %d %d",&a[i].xl,&a[i].yl,&a[i].xr,&a[i].yr);
            sumx+=a[i].xr-a[i].xl;
            sumy+=a[i].yr-a[i].yl;
            x[cntx++]=a[i].xl;x[cntx++]=a[i].xr;
            y[cnty++]=a[i].yl;y[cnty++]=a[i].yr;
        }
        sort(x+1,x+cntx);
        sort(y+1,y+cnty);
       // bug2(cntx,cnty);
       --cntx;--cnty;
        ll xx=x[cntx/2],yy=y[cnty/2];
        ll ans=-sumx-sumy;
        for(int i =1;i<=n;i++)
        {
            ans+=abs(a[i].xl-xx)+abs(xx-a[i].xr);
            ans+=abs(a[i].yl-yy)+abs(yy-a[i].yr);
        }
        printf("%lld\n",ans/2);
     }
    return 0;
}

除了中位数外,我们可以看出这个一维情况下,这个函数是个单峰函数,可以用三分分别求x,y的位置 然后再算总和

#include<bits/stdc++.h>
#define bug1(g) cout<<"test: "<<g<<endl
#define bug2(g,i) cout<<"test: "<<g<<" "<<i<<endl
#define bug3(g,i,k) cout<<"test: "<<g<<" "<<i<<" "<<k<<endl
#define bug4(a,g,i,k) cout<<"test: "<<a<<" "<<g<<" "<<i<<" "<<k<<endl
using namespace std;
typedef long long ll;
const int maxn=100005;
int n;
struct node
{
   int xl,xr,yl,yr;
}a[maxn];
int maxx[2],minn[2];
ll sum(int x,int ii)
{
    ll ans=0;
    if(ii==0) //x
        for(int i=1;i<=n;i++)
        {
            if(a[i].xl<=x&&x<=a[i].xr) continue;
            else if(x<a[i].xl) ans+=(ll)a[i].xl-x;
            else ans+=(ll)x-a[i].xr;
        }
    else
    {
            for(int i=1;i<=n;i++)
        {
            if(a[i].yl<=x&&x<=a[i].yr) continue;
            else if(x<a[i].yl) ans+=(ll)a[i].yl-x;
            else ans+=(ll)x-a[i].yr;
        }
    }
    return ans;
}
int sery(int minn,int maxx,int i)
{
    int l =minn,r=maxx;
    int mid1,mid2;
    while(r-l>1)
    {
        mid1=(l+r)/2;
        mid2=(mid1+r)/2;
        ll m1=sum(mid1,i),m2=sum(mid2,i);
        if(m1<m2)
            r=mid2;
        else l=mid1;
    }
    return sum(l,i)<sum(r,i)?l:r;
}
int t;
int main()
{
    //ios::sync_with_stdio(0);
     scanf("%d",&t);
     while(t--){
        scanf("%d",&n);
        maxx[0]=maxx[1]=-1;
        minn[0]=minn[1]=1e9+5;
        for(int i =1;i<=n;i++)
        {
            scanf("%d %d %d %d",&a[i].xl,&a[i].yl,&a[i].xr,&a[i].yr);
            maxx[0]=max(maxx[0],a[i].xr);
            maxx[1]=max(maxx[1],a[i].yr);
            minn[0]=min(minn[0],a[i].xl);
            minn[1]=min(minn[1],a[i].yl);
        }
        int xx=sery(minn[0],maxx[0],0),yy=sery(minn[1],maxx[1],1);
        //bug2(xx,yy);
        cout<<sum(xx,0)+sum(yy,1)<<endl;
     }
    return 0;
}

C题: 给N条直线,求有多少对直线有交点(重合也算)

这题卡了精度,用斜率表示一直过不去 ,最后把直线写成AX+BY+C的形式 判断有多少直线重合 有多少重合或平行
假设一共有n条直线 xi条重合 yi条重合或平行 ans=n*(n-1)/2-yi*(yi-1)/2+xi*(xi-1)/2
重合要求A,B,C三个参数都一样,平行或重合只需要A,B一样就行,用map记录一下就ok了

#include<bits/stdc++.h>
#define bug1(g) cout<<"test: "<<g<<endl
#define bug2(g,i) cout<<"test: "<<g<<" "<<i<<endl
#define bug3(g,i,k) cout<<"test: "<<g<<" "<<i<<" "<<k<<endl
#define bug4(a,g,i,k) cout<<"test: "<<a<<" "<<g<<" "<<i<<" "<<k<<endl
using namespace std;
typedef long long ll;
const int maxn=100005;
struct node
{
   ll a,b,c;
    friend bool operator <(node a,node b)
    {
        if(a.a!=b.a)return a.a<b.a;
        else if(a.b!=b.b) return a.b<b.b;
        return a.c<b.c;
    }
}a[maxn],b;
map<node,ll>mp1,mp2;
ll t,n;
int main()
{
    ios::sync_with_stdio(0);
    cin>>t;
    while(t--)
    {
        ll xa,ya,xb,yb;
        mp1.clear();
        mp2.clear();
        cin>>n;
        for(int i =1;i<=n;i++)
        {
            cin>>xa>>ya>>xb>>yb;
            yb=ya-yb;xb=xa-xb;
            ll k =__gcd(yb,xb);
            yb/=k;xb/=k;
            a[i].a=xb;
            a[i].b=-yb;
            a[i].c=yb*xa-xb*ya;
            if(a[i].a<0)
            {
                a[i].a=-a[i].a;
                a[i].b=-a[i].b;
                a[i].c=-a[i].c;
            }
            b.a=a[i].a,b.b=a[i].b,b.c=0;
            ++mp1[a[i]];++mp2[b];
        }
        ll ans=n*(n-1)/2;
        for(auto i =mp1.begin();i!=mp1.end();i++)
        {
            ll tmp=i->second;
            ans+=tmp*(tmp-1)/2;
        }
         for(auto i =mp2.begin();i!=mp2.end();i++)
        {
            ll tmp=i->second;
            ans-=tmp*(tmp-1)/2;
        }
        cout<<ans<<endl;

    }
    return 0;
}

H题:在这里插入图片描述

队友写的,我也不会 贴个代码吧

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
struct Point {
	ll x, y;
	bool operator<( Point const& b) const{
		return x<b.x||(x==b.x&&y<b.y);
	}
};
struct P {
	Point a, b;
	bool operator<( P  const& b) const{
		return a < b.a ;
	}
};
map<P, int> mp;
map<Point, int> mp2;
ll gcd(ll a, ll b) {
	return b == 0 ? a : gcd(b, a%b);
}
Point solve(Point &a,Point &b) {
	ll detx = b.x - a.x;
	ll dety = b.y - a.y;
	ll gcdd = gcd(detx, dety);
//	cout << gcdd << endl;
	detx /= gcdd;
	dety /= gcdd;
	if (detx == 0) {
		a.y = b.y = 0;
		return Point{-1,-1};
	}
	if (dety == 0) {
		a.x = b.x = 0;
		return Point{0,0};
	}
	if (detx < 0) {
		detx = -detx;
		dety = -dety;
	}

	if (a.x >= 0) {
		ll tem = a.x / detx;
		a.x -= detx * tem;
		a.y -= dety * tem;
	}
	else {
		ll tem = -a.x / detx + 1;
		a.x += detx * tem;
		a.y += dety * tem;
	}
	if (b.x >= 0) {
		ll tem = b.x / detx;
		b.x -= detx * tem;
		b.y -= dety * tem;
	}
	else {
		ll tem = -b.x / detx + 1;
		b.x += detx * tem;
		b.y += dety * tem;
	}
	return Point{ detx,dety };
}
int main() {
	int T;
	scanf("%d", &T);
	while (T--) {
		ll ans = 0;
		mp.clear();
		mp2.clear();
		int n;
		scanf("%d", &n);
		for (int i = 0; i < n; i++) {
			ll x1, y1, x2, y2;
			scanf("%lld%lld%lld%lld", &x1, &y1, &x2, &y2);
			Point a = Point{ x1,y1 };
			Point b = Point{ x2,y2 };
			Point p=solve(a, b);
			if (a < b) swap(a, b);
			P p2 = P{ a,b };
			ans += i - mp2[p] + mp[p2];
			mp2[p]++;
			mp[p2]++;
		}
		printf("%I64d\n", ans);
	}
}

B题:有n个糖果m种,每个糖果有两个值ai,bi 代表价值和种类,选择一些糖果,问选择糖果的价值和除以最大的同一种类数的值最大,第i种糖果至少选择l[i]次

枚举分母,选最大的分子就好了,预处理下选择x个糖果的情况下能获得的最大价值和(贪心先选择价值大的)

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
ll n,m;
ll t;
vector<ll>v[100005]; //每种中的价值有哪些
ll l[100005];
ll s,c;
ll ms[100005];
bool cmp(ll a,ll b)
{
    return a>b;
}
int main()
{
    ios::sync_with_stdio(0);
    cin>>t;
    while(t--)
    {
        cin>>n>>m;
        for(int i =1;i<=m;i++)
        {
            cin>>l[i];
            v[i].clear();
        }
        memset(ms,0,sizeof(ll)*(n+2));
        ll x,y;
        for(int i =1;i<=n;i++)
        {
            cin>>x>>y;
            v[y].push_back(x);
        }
        ll maxx=-1;
        for(int i =1;i<=m;i++)
        {
            maxx=max((ll)v[i].size(),maxx);
            sort(v[i].begin(),v[i].end(),cmp);
            ll tmp=0;
            for(int j = 0;j<v[i].size();j++)
            {
                tmp+=v[i][j];
                if(j+1>=l[i])   //小于l[i]时不算在价值和里
                 {
                     ms[j+1]+=tmp;
                     tmp=0;
                 }
            }
        }
        ll as=-1,ac=1;
        for(ll i =1;i<=maxx;i++) //枚举同种最大数量
        {
            ms[i]+=ms[i-1];
            if(ms[i]!=0)
            {
                s=ms[i];c=i;
                ll g=__gcd(s,c);
                s/=g;c/=g;
                if(as*c<ac*s)
                {
                    as=s;
                    ac=c;
                }
            }
        }
        cout<<as<<"/"<<ac<<endl;

    }
}

E题: 给一颗树,问其线图的最小生成树

观察发现,原来的边权相当于线图的点权,线图的边权为两个结点的点权和. 而且对线图来说,他是一些完全图连接的,对完全图求最小生成树只需要将其余点都连向点权最小的点就行

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int t;
int n;
struct node
{
    ll v,w;
    node(ll a=0,ll b=0)
    {
        v=a,w=b;
    }
} ;
vector<node>a[100005];
int main()
{
    ios::sync_with_stdio(0);
    cin>>t;
    while(t--)
    {
        cin>>n;
        ll x,y,z;
        for(int i =1;i<=n;i++) a[i].clear();
        for(int i =1;i<=n-1;i++)
        {
            cin>>x>>y>>z;
            a[x].push_back(node(y,z));
            a[y].push_back(node(x,z));
        }
        ll ans=0;
        for(int i =1;i<=n;i++)
        {
            if(a[i].size()<2) continue;
            ll minn=0;
            for(int j =0;j<a[i].size();j++)
            {
                if(a[i][j].w<a[i][minn].w)  minn=j;
            }
            for(int j = 0;j<a[i].size();j++)
            {
                if(j!=minn)
                    ans+=a[i][j].w+a[i][minn].w;
            }
        }
        cout<<ans<<endl;
    }
    return 0;
}
  • 5
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值