第 45 届国际大学生程序设计竞赛(ICPC)亚洲区域赛(昆明)HIJL待续

本文介绍了四道编程竞赛题目,涉及ICPC亚洲区域赛、图的染色问题、风车问题和并行排序。在图的染色问题中,使用线段树求解逆序对;风车问题通过计算几何求解线段交点;并行排序探讨了如何减少交换次数达到快速排序效果。文章提供了详细的解题思路和C++代码实现。
摘要由CSDN通过智能技术生成

第 45 届国际大学生程序设计竞赛(ICPC)亚洲区域赛(昆明)
五四青年节补补上次昆明的题目,熟悉下区域赛的难度和类型。
还没写好,因为要睡觉了,等以后再补坑把

H.Hard Calculation

题意:真签到题。第1年举办这次昆明ICPC比赛是2021年,问第n年具体的年份是多少?

输出:2020+n

L.Simone and graph coloring

题意:当i<j且a[i]>a[j],存在点i到点j的一条边,现在需要对图进行染色,且相邻两个点的颜色不一样

题解:即存在逆序对时才会有边,当然也存在孤独的一个点,线段树/树状数组类似求逆序对。

对于点a[i],如果[0,1,2…n-1]存在值为[a[i]+1,a[i+2]…n]的点,则需要找当前颜色种类最大值+1

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define endl "\n"
const ll MAX=1e6+7;
const ll inf=0x3f3f3f3f;
ll a[MAX],ans[MAX],n,cnt;
struct node{
	ll ma;
}tree[MAX<<4]; 
void build(ll l=1,ll r=n,ll p=1){
	tree[p].ma=0;
	if(l==r)return ;
	ll mid=(l+r)/2;
	build(l,mid,2*p);
	build(mid+1,r,2*p+1);
}
void update(ll l,ll r,ll val,ll tl=1,ll tr=n,ll p=1){
	if(tl>=l&&tr<=r){
		tree[p].ma+=val;	
		return ;
	}
	ll mid=(tl+tr)/2; 
	if(mid>=l)update(l,r,val,tl,mid,2*p);
	if(mid<r)update(l,r,val,mid+1,tr,2*p+1);
	tree[p].ma=max(tree[2*p].ma,tree[p*2+1].ma);
} 
ll getmax(ll l,ll r,ll tl=1,ll tr=n,ll p=1){
	if(tl>=l&&tr<=r)return tree[p].ma;
	ll mid=(tl+tr)/2;
	ll res=-inf;
	if(mid>=l)res=max(res,getmax(l,r,tl,mid,2*p));
	if(mid<r)res=max(res,getmax(l,r,mid+1,tr,2*p+1));
	return res; 
}
signed main(){
	ios_base::sync_with_stdio(0);cin.tie(0),cout.tie(0);
	int t;cin>>t;
	while(t--){
		cin>>n; cnt=0;
		for(int i=1;i<=n;i++)cin>>a[i],ans[i]=0; 
		build();
		for(int i=1;i<=n;i++){
			ll x=getmax(a[i],n);
			ans[i]=x+1;
			update(a[i],a[i],ans[i]);
		}
		for(int i=1;i<=n;i++)cnt=max(cnt,ans[i]);
		cout<<cnt<<endl;
		for(int i=1;i<=n;i++)cout<<ans[i]<<" ";
		cout<<endl;
	}
	return 0;
}

I.Mr. Main and Windmills

题意:从起点(xs,ys)沿线段到达(xt,yt),分别给出直线的一边有n个星星的坐标。问,在沿线段的途中对于点hi与当前点连直线,问直线左右点数变化了点k个时最早出现的位置
在这里插入图片描述

题解:计算几何,n^2求出每两个点所构成与原来线段的交点,按照从起点到该交点距离大小进行升序排序

对于点h,左右变化k次,答案即使是点h与其他点所构成直线与原线段交点排序后的数组中,第k个点,若该数组大小小于k则输出-1。

主要注意的是esp精度不要大小,且需要注意两点之间所构造的直线需要注意不存在斜率的情况。

//#pragma GCC optimize(2)
#include<bits/stdc++.h> 
using namespace std;
#define endl "\n" 
#define ll long long
const int N=1e3+10;

struct Point{
    double x,y;
    double dis;
    Point(double x=0,double y=0):x(x),y(y){}
};

typedef Point Vector;

Vector operator + (Vector A,Vector B) {
    return Vector(A.x+B.x,A.y+B.y);
}
Vector operator - (Vector A,Vector B) {
    return Vector(A.x-B.x,A.y-B.y);
}
Vector operator * (Vector A,double p) {
    return Vector(A.x*p,A.y*p);
}

Vector operator / (Vector A,double p) {
    return Vector(A.x/p,A.y/p);
}
bool operator < (const Point &a,const Point &b){
    return a.x<b.x||(a.x==b.x&&a.y<b.y);
}
const double eps=1e-10;

int dcmp(double x){
    if(fabs(x)<eps)return 0;
    else return x<0?-1:1;
}

bool operator ==(const Point& a,const Point &b){
    return dcmp(a.x-b.x)==0&&dcmp(a.y-b.y)==0;
}

double Dot(Vector A,Vector B){
    return A.x*B.x+A.y*B.y;
}

double Length(Vector A){
    return sqrt(Dot(A,A));
}

double Angle(Vector A,Vector B) {
    return acos(Dot(A,B)/Length(A)/Length(B));
}

double Cross(Vector A,Vector B){
    return A.x*B.y-A.y*B.x;
}
Vector Rotate(Vector A,double rad){
    return Vector(A.x*cos(rad)-A.y*sin(rad),A.x*sin(rad)+A.y*(rad));
}

Point GetLineIntersection(Point P,Vector v,Vector Q,Vector w){
    Vector u=P-Q;
    double t=Cross(w,u)/Cross(v,w);
    return P+v*t;
}

bool SegmentProperInterSection(Point a1,Point a2,Point b1,Point b2){
    double c1=Cross(a2-a1,b1-a1),c2=Cross(a2-a1,b2-a1);
    double c3=Cross(b2-b1,a1-b1),c4=Cross(b2-b1,a2-b1);
    return dcmp(c1)*dcmp(c2)<0&&dcmp(c3)*dcmp(c4)<0;
}

bool OnSegment(Point p,Point a1,Point a2){
    return dcmp(Dot(a1-p,a2-p))<=0;
}

double length(Point &a,Point &b){
    return sqrt(pow(fabs(a.x-b.x),2)+pow(fabs(a.y-b.y),2));
}
struct node{
    Point p;
    vector<Point> Node;
}a[N];
bool cmp(Point a,Point b){
    return a.dis<b.dis;
}
signed main(){
    ios_base::sync_with_stdio(0);cin.tie(0),cout.tie(0);
    int n,m; cin>>n>>m;
    Point S,T; cin>>S.x>>S.y>>T.x>>T.y;
    for(int i=1;i<=n;i++)cin>>a[i].p.x>>a[i].p.y;
    for(int i=1;i<=n;i++){
        for(int j=1;j<=n;j++){
            if(i==j)continue;
            Point temp; //
            Vector v=a[i].p-a[j].p;
            Point P=a[i].p;  //
            Vector w=S-T;
            temp=GetLineIntersection(P,v,S,w);
            temp.dis=length(temp,S);
            if(OnSegment(temp,S,T))a[i].Node.push_back(temp);
        }
    }
    while(m--){
        int h,k;cin>>h>>k;
        if(a[h].Node.size()<k)cout<<-1<<endl;
        else{
            sort(a[h].Node.begin(),a[h].Node.end(),cmp);
            Point X=a[h].Node[k-1];
            cout<<fixed<<setprecision(10)<<X.x<<" "<<X.y<<endl;
        }
    }
   return 0;
} 

J.Parallel Sort

题意:数列p{p1,p2,p3…,pn}需要类似快速排序一般,进行两两交换,每次可以交换即将到出现轮回为止,问至少需要多少轮回交换,并输出每次交换的次数和操作

题解:可以考虑成图,在p[i]!=i的点中,每次打标记dfs找到最长的一个环,最优的情况是对于每个pi到p[p[i]]之间存在交换边,

且该两点之间只要交换一次就可以缩小一个环,所以可以考虑所有的环都变成大小小于等于2的环

//#pragma GCC optimize(2)
#include<bits/stdc++.h> 
using namespace std;
#define endl "\n" 
#define ll long long
const int N=1e3+7;
const int mod=1e9+7;
const ll inf=0x3f3f3f3f;
const double pi=acos(-1);
//#define int long long
vector<vector<pair<int,int>>>ans;
signed main(){
    ios_base::sync_with_stdio(0);cin.tie(0),cout.tie(0);
    int n;cin>>n;
    vector<int>p(n);
    for(int i=1;i<=n;i++)cin>>p[i];
    while(1){
        vector<int>vis(n+2,0);
        vector<pair<int,int>>change;
        for(int i=1;i<=n;i++){
            if(p[i]==i||vis[i])continue;
            vector<int>res;
            int u=i;
            while(!vis[u]){
                res.push_back(u);
                vis[u]=1; u=p[u];
            }
            int l=0,r=res.size()-1;
            while(l<r){
                swap(p[res[l]],p[res[r]]);
                change.push_back({res[l],res[r]});
                l++;r--;
            }
        }
        if(change.size()>=1)ans.push_back(change);
        else break;
    }
    cout<<ans.size()<<endl;
    for(auto x:ans){
        cout<<x.size()<<" ";
        for(auto y:x) cout<<y.first<<" "<<y.second<<" ";
        cout<<endl;
    }
   return 0;
} 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值