第 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;
}