您的位置:首页 > 其它

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

2019-05-27 12:23 387 查看

比赛链接 :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

+ mp[p2]; mp2[p]++; mp[p2]++; } printf("%I64d\n", ans); } }

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

[p]枚举分母,选最大的分子就好了,预处理下选择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;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐