2020 ICPC 上海站(gym 102900) 题解

2020 ICPC 上海站(gym 102900) 题解

Problem H Rice Arrangement

这题相对而言比较简单。

假设我们直到了每一碗饭能分给的人的区间,然后就可以发现,将这些区间排出来一定是这样:

[-------]-----
--[-------]---
-----[-------]

也就是右端点随左端点递增而递增。

如果我们将饭和人链接一条线。

大概是这样:

o----o------o-----o
|     \    /     /
x------x---x---x---

可以发现不可能出现这样的情况:

o-o--------
 V
/ \  
x---x------

由于覆盖的人可以看作区间。

所以确定了第一个人吃哪一碗饭,其它的都确定了。

我们先假设所有的都想左转,然后将度数排序。可以发现一定是一段后缀向右转。

时间复杂度为\(O(N^2\log N)\)

/*
{
######################
#       Author       #
#        Gary        #
#        2021        #
######################
*/
#include<bits/stdc++.h>
#define rb(a,b,c) for(int a=b;a<=c;++a)
#define rl(a,b,c) for(int a=b;a>=c;--a)
#define LL long long
#define IT iterator
#define PB push_back
#define II(a,b) make_pair(a,b)
#define FIR first
#define SEC second
#define FREO freopen("check.out","w",stdout)
#define rep(a,b) for(int a=0;a<b;++a)
#define SRAND mt19937 rng(chrono::steady_clock::now().time_since_epoch().count())
#define random(a) rng()%a
#define ALL(a) a.begin(),a.end()
#define POB pop_back
#define ff fflush(stdout)
#define fastio ios::sync_with_stdio(false)
#define check_min(a,b) a=min(a,b)
#define check_max(a,b) a=max(a,b)
using namespace std;
//inline int read(){
//    int x=0;
//    char ch=getchar();
//    while(ch<'0'||ch>'9'){
//        ch=getchar();
//    }
//    while(ch>='0'&&ch<='9'){
//        x=(x<<1)+(x<<3)+(ch^48);
//        ch=getchar();
//    }
//    return x;
//}
const int INF=0x3f3f3f3f;
typedef pair<int,int> mp;
/*}
*/
int n,t;
int Q;
int a[1001],b[1001];
int dist(int A,int B){
	if(A<=B) return B-A;
	return n-A+B;
}
void solve(){
	scanf("%d%d",&n,&t);
	rep(i,t) scanf("%d",&a[i]);
	rep(i,t) scanf("%d",&b[i]);
	sort(a,a+t);
	sort(b,b+t);
	int ret=INF;
	rep(i,t)
	{
		vector<int> v;
		rep(j,t){
			v.PB(dist(b[(i+j)%t],a[j]));
		}
		sort(ALL(v));
		int tmp=min(v[t-1],n-v[0]);
		rep(j,t-1){
			check_min(tmp,min(v[j]+v[j]+n-v[j+1],v[j]+2*n-v[j+1]-v[j+1]));
		}
		check_min(ret,tmp);
	}
	printf("%d\n",ret);
}
int main(){
	scanf("%d",&Q);
	while(Q--) solve();
	return 0;
}

Problem L Traveling in the Grid World

考虑这题的加强:

\[T\leq 1e6,n,m\leq 1e18 \]

也就是要\(O(1)\)回答询问。

首先可以发现如果\((n,m)=1\)则一步到达。

否则我们只需要走最近的折线就一定不会存在格点。可以证明最优折线和对角线形成的三角形及其内部不存在格点,否则可以调整法变成更优答案。

然后原问题就可以迎刃而解了,只要枚举横坐标,然后选择最靠近对角线的点就可以了,时间复杂度为\(O(N)\)。

但是这题可以做到更优。

设\(x,y=n/(n,,m),m/(n,m)\)。

一个结论:
靠对角线最近的格点中最靠近中点的最优。

根据叉积的定义:

就是这个值最小的点(a,b):

\[x*b-y*m/(\sqrt {x^2+y^2}) \]

后面的那个是常数,不用管,前面的那个最小值为\(1\),直接exgcd求解就可以了。

可以发现,所有距离\(> 1/(\sqrt{x^2+y^2})\)的至少为那个距离的两倍。

根据中位线可以发现一定存在格点,所以距离最近的中最靠近中间的最优。

code:

/*
{
######################
#       Author       #
#        Gary        #
#        2021        #
######################
*/
#include<bits/stdc++.h>
#define rb(a,b,c) for(int a=b;a<=c;++a)
#define rl(a,b,c) for(int a=b;a>=c;--a)
#define LL long long
#define IT iterator
#define PB push_back
#define II(a,b) make_pair(a,b)
#define FIR first
#define SEC second
#define FREO freopen("check.out","w",stdout)
#define rep(a,b) for(int a=0;a<b;++a)
#define SRAND mt19937 rng(chrono::steady_clock::now().time_since_epoch().count())
#define random(a) rng()%a
#define ALL(a) a.begin(),a.end()
#define POB pop_back
#define ff fflush(stdout)
#define fastio ios::sync_with_stdio(false)
#define check_min(a,b) a=min(a,b)
#define check_max(a,b) a=max(a,b)
using namespace std;
//inline int read(){
//    int x=0;
//    char ch=getchar();
//    while(ch<'0'||ch>'9'){
//        ch=getchar();
//    }
//    while(ch>='0'&&ch<='9'){
//        x=(x<<1)+(x<<3)+(ch^48);
//        ch=getchar();
//    }
//    return x;
//}
const int INF=0x3f3f3f3f;
typedef pair<int,int> mp;
/*}
*/
int T;
LL exGcd(LL a,LL b,LL &x,LL &y)
{
    if(b==0)
    {
        x = 1;
        y = 0;
        return a;
    }
    LL r = exGcd(b,a%b,x,y);
    LL t = x; x = y;
    y = t-a/b*y;
    return r;
}
double dist(int x,int y){
	double tmp=1ll*x*x+1ll*y*y;
	return sqrt(tmp);
}
void solve(){
	int x,y;
	scanf("%d%d",&x,&y);
	if(__gcd(x,y)==1){
		printf("%.10f\n",dist(x,y));
		return;
	}
	int g=__gcd(x,y);
	int n=x,m=y;
	x=n/g;
	y=m/g;
	LL a,b;
	exGcd(x,y,b,a);
	a=-a;
	LL need=max(0ll,max((x-a)/x,(y-b)/y));
	a+=need*x;
	b+=need*y;
	need=min((a-1)/x,(b-1)/y);
	a-=need*x;
	b-=need*y;
	double rest=1e18;
	LL can=min((n-a)/x,(m-b)/y);
	can/=2;
	for(LL i=can-5;i<=can+5;++i){
		LL xx,yy;
		xx=a;
		yy=b;
		xx+=x*i;
		yy+=y*i;
		if(xx>=0&&yy>=0&&xx<=n&&yy<=m){
			check_min(rest,dist(xx,yy)+dist(n-xx,m-yy));
		}
	}
	printf("%.10f\n",rest);
}
int main(){
	scanf("%d",&T);
	while(T--) solve();	
	return 0;
}

上一篇:【瞎口胡】整体二分


下一篇:leetcode 567 滑动窗口